Skip to content

Commit e315496

Browse files
committed
Switch angle sensors to use integer instead of floating point
1 parent 9608741 commit e315496

17 files changed

Lines changed: 177 additions & 35 deletions

src/common/base_classes/FOCMotor.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@ float FOCMotor::shaftVelocity() {
7777
float FOCMotor::electricalAngle(){
7878
// if no sensor linked return previous value ( for open loop )
7979
if(!sensor) return electrical_angle;
80-
return _normalizeAngle( (float)(sensor_direction * pole_pairs) * sensor->getMechanicalAngle() - zero_electric_angle );
80+
return _normalizeAngle( (float)(sensor_direction * pole_pairs) * sensor->getMechanicalAngle()
81+
#ifdef INTEGER_ANGLE
82+
* _2PI / sensor->getStepsPerRevolution()
83+
#endif
84+
- zero_electric_angle );
8185
}
8286

8387
/**
@@ -289,8 +293,16 @@ int FOCMotor::characteriseMotor(float voltage, float correction_factor=1.0f){
289293
* We then report the one closest to the actual value. This could be useful if the zero search method is not reliable enough (eg. high pole count).
290294
*/
291295

292-
float estimated_zero_electric_angle_A = _normalizeAngle( (float)(sensor_direction * pole_pairs) * sensor->getMechanicalAngle() - d_electrical_angle);
293-
float estimated_zero_electric_angle_B = _normalizeAngle( (float)(sensor_direction * pole_pairs) * sensor->getMechanicalAngle() - d_electrical_angle + _PI);
296+
float estimated_zero_electric_angle_A = _normalizeAngle( (float)(sensor_direction * pole_pairs) * sensor->getMechanicalAngle()
297+
#ifdef INTEGER_ANGLE
298+
* _2PI / sensor->getStepsPerRevolution()
299+
#endif
300+
- d_electrical_angle);
301+
float estimated_zero_electric_angle_B = _normalizeAngle( (float)(sensor_direction * pole_pairs) * sensor->getMechanicalAngle()
302+
#ifdef INTEGER_ANGLE
303+
* _2PI / sensor->getStepsPerRevolution()
304+
#endif
305+
- d_electrical_angle + _PI);
294306
float estimated_zero_electric_angle = 0.0f;
295307
if (fabsf(estimated_zero_electric_angle_A - zero_electric_angle) < fabsf(estimated_zero_electric_angle_B - zero_electric_angle))
296308
{

src/common/base_classes/Sensor.cpp

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,48 +5,69 @@
55

66

77
void Sensor::update() {
8-
float val = getSensorAngle();
8+
angle_type val = getSensorAngle();
99
if (val<0) // sensor angles are strictly non-negative. Negative values are used to signal errors.
1010
return; // TODO signal error, e.g. via a flag and counter
1111
angle_prev_ts = _micros();
12-
float d_angle = val - angle_prev;
13-
// if overflow happened track it as full rotation
14-
if(abs(d_angle) > (0.8f*_2PI) ) full_rotations += ( d_angle > 0 ) ? -1 : 1;
15-
angle_prev = val;
12+
setAngleContinuous(val);
1613
}
1714

1815

1916
/** get current angular velocity (rad/s) */
2017
float Sensor::getVelocity() {
2118
// calculate sample time
2219
// if timestamps were unsigned, we could get rid of this section, unsigned overflow handles it correctly
20+
#ifdef INTEGER_ANGLE
21+
angle_type Ts = angle_prev_ts - vel_angle_prev_ts;
22+
#else
2323
float Ts = (angle_prev_ts - vel_angle_prev_ts)*1e-6f;
24+
#endif
25+
#if 0
2426
if (Ts < 0.0f) { // handle micros() overflow - we need to reset vel_angle_prev_ts
2527
vel_angle_prev = angle_prev;
28+
#ifndef INTEGER_ANGLE
2629
vel_full_rotations = full_rotations;
30+
#endif
2731
vel_angle_prev_ts = angle_prev_ts;
2832
return velocity;
2933
}
34+
#endif
3035
if (Ts < min_elapsed_time) return velocity; // don't update velocity if deltaT is too small
3136

3237
// Calculate change in angle. Handles `full_rotations` integer wrap-arounds,
3338
// and avoids float precision loss issues by keeping numbers small.
34-
float delta_angle = angle_prev - vel_angle_prev;
39+
angle_type delta_angle = angle_prev - vel_angle_prev;
40+
#ifndef INTEGER_ANGLE
3541
const int32_t delta_full_rotations = full_rotations - vel_full_rotations;
3642
if (delta_full_rotations) {
3743
delta_angle += delta_full_rotations * _2PI;
3844
}
45+
#endif
3946

47+
#ifdef INTEGER_ANGLE
48+
if (abs(delta_angle) > 0)
49+
{
50+
velocity = delta_angle * 1000000 * _2PI / (Ts * steps_per_revolution);
51+
}
52+
else
53+
{
54+
velocity = 0.0f;
55+
}
56+
57+
#else
4058
// floating point equality checks are bad, so instead we check that the angle change is very small
4159
if (fabsf(delta_angle) > 1e-8f) {
4260
velocity = delta_angle / Ts;
4361
} else {
4462
velocity = 0.0f;
4563
}
64+
#endif
4665

4766
// Always advance the velocity reference sample to avoid stale deltas/time windows.
4867
vel_angle_prev = angle_prev;
68+
#ifndef INTEGER_ANGLE
4969
vel_full_rotations = full_rotations;
70+
#endif
5071
vel_angle_prev_ts = angle_prev_ts;
5172

5273
return velocity;
@@ -68,30 +89,63 @@ void Sensor::init() {
6889
}
6990

7091

71-
float Sensor::getMechanicalAngle() {
92+
angle_type Sensor::getMechanicalAngle() {
7293
return angle_prev;
7394
}
7495

7596

7697

7798
float Sensor::getAngle(){
99+
#ifdef INTEGER_ANGLE
100+
return angle_prev / (float)steps_per_revolution * _2PI;
101+
#else
78102
return (float)full_rotations * _2PI + angle_prev;
103+
#endif
79104
}
80105

81106

82107

83108
double Sensor::getPreciseAngle() {
109+
#ifdef INTEGER_ANGLE
110+
return angle_prev / (double)steps_per_revolution * _2PI;
111+
#else
84112
return (double)full_rotations * (double)_2PI + (double)angle_prev;
113+
#endif
85114
}
86115

87116

88117

89118
int32_t Sensor::getFullRotations() {
119+
#ifdef INTEGER_ANGLE
120+
return angle_prev/steps_per_revolution;
121+
#else
90122
return full_rotations;
123+
#endif
91124
}
92125

93126

94127

95128
int Sensor::needsSearch() {
96129
return 0; // default false
97130
}
131+
132+
void Sensor::setAngleContinuous(angle_type sensor_angle)
133+
{
134+
#ifdef INTEGER_ANGLE
135+
angle_type d_angle = sensor_angle - sensor_angle_prev;
136+
if(abs(d_angle) > (steps_per_revolution/2) )
137+
{
138+
d_angle += d_angle > 0 ? -steps_per_revolution : steps_per_revolution;
139+
}
140+
angle_prev += d_angle;
141+
sensor_angle_prev = sensor_angle;
142+
#else
143+
angle_type d_angle = sensor_angle - angle_prev;
144+
if(abs(d_angle) > (0.8f*_2PI) )
145+
{
146+
full_rotations += ( d_angle > 0 ) ? -1 : 1;
147+
}
148+
angle_prev = sensor_angle;
149+
#endif
150+
151+
}

src/common/base_classes/Sensor.h

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ enum Pullup : uint8_t {
2121
USE_EXTERN = 0x01 //!< Use external pullups
2222
};
2323

24+
#define INTEGER_ANGLE
25+
26+
#ifdef INTEGER_ANGLE
27+
using angle_type = int;
28+
#else
29+
using angle_type = float;
30+
#endif
31+
2432
/**
2533
* Sensor abstract class defintion
2634
*
@@ -43,13 +51,15 @@ enum Pullup : uint8_t {
4351
*/
4452
class Sensor{
4553
friend class SmoothingSensor;
54+
friend class CalibratedSensor;
55+
friend class HysteresisSensor;
4656
public:
4757
/**
4858
* Get mechanical shaft angle in the range 0 to 2PI. This value will be as precise as possible with
4959
* the hardware. Base implementation uses the values returned by update() so that
5060
* the same values are returned until update() is called again.
5161
*/
52-
virtual float getMechanicalAngle();
62+
virtual angle_type getMechanicalAngle();
5363

5464
/**
5565
* Get current position (in rad) including full rotations and shaft angle.
@@ -108,6 +118,10 @@ class Sensor{
108118
*/
109119
float min_elapsed_time = 0.000100f; // default is 100 microseconds, or 10kHz
110120

121+
#ifdef INTEGER_ANGLE
122+
int getStepsPerRevolution() const {return steps_per_revolution;};
123+
#endif
124+
111125
protected:
112126
/**
113127
* Get current shaft angle from the sensor hardware, and
@@ -117,7 +131,8 @@ class Sensor{
117131
* Calling this method directly does not update the base-class internal fields.
118132
* Use update() when calling from outside code.
119133
*/
120-
virtual float getSensorAngle()=0;
134+
virtual angle_type getSensorAngle()=0;
135+
121136
/**
122137
* Call Sensor::init() from your sensor subclass's init method if you want smoother startup
123138
* The base class init() method calls getSensorAngle() several times to initialize the internal fields
@@ -126,14 +141,26 @@ class Sensor{
126141
*/
127142
virtual void init();
128143

144+
/**
145+
* Set angle_prev and full_rotations from a sensor angle, handling sensor overflows as revolutions
146+
*/
147+
void setAngleContinuous(angle_type sensor_angle);
148+
129149
// velocity calculation variables
130150
float velocity=0.0f;
131-
float angle_prev=0.0f; // result of last call to getSensorAngle(), used for full rotations and velocity
132-
long angle_prev_ts=0; // timestamp of last call to getAngle, used for velocity
151+
angle_type angle_prev=0.0f; // result of last call to getSensorAngle(), used for full rotations and velocity
152+
unsigned long angle_prev_ts=0; // timestamp of last call to getAngle, used for velocity
133153
float vel_angle_prev=0.0f; // angle at last call to getVelocity, used for velocity
134-
long vel_angle_prev_ts=0; // last velocity calculation timestamp
154+
unsigned long vel_angle_prev_ts=0; // last velocity calculation timestamp
155+
#ifdef INTEGER_ANGLE
156+
private:
157+
angle_type sensor_angle_prev = 0;
158+
protected:
159+
angle_type steps_per_revolution = 2;
160+
#else
135161
int32_t full_rotations=0; // full rotation tracking
136162
int32_t vel_full_rotations=0; // previous full rotation value for velocity calculation
163+
#endif
137164
};
138165

139166
#endif

src/sensors/Encoder.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ Encoder::Encoder(int _encA, int _encB , float _ppr, int _index){
1919
pulse_timestamp = 0;
2020

2121
cpr = _ppr;
22+
#ifdef INTEGER_ANGLE
23+
steps_per_revolution = cpr;
24+
#endif
2225
A_active = 0;
2326
B_active = 0;
2427
I_active = 0;
@@ -106,16 +109,25 @@ void Encoder::update() {
106109
angle_prev_ts = pulse_timestamp;
107110
long copy_pulse_counter = pulse_counter;
108111
interrupts();
112+
#ifdef INTEGER_ANGLE
113+
setAngleContinuous(copy_pulse_counter%steps_per_revolution);
114+
#else
109115
// TODO: numerical precision issue here if the pulse_counter overflows the angle will be lost
110116
full_rotations = copy_pulse_counter / (int)cpr;
111117
angle_prev = _2PI * ((copy_pulse_counter) % ((int)cpr)) / ((float)cpr);
118+
#endif
119+
112120
}
113121

114122
/*
115123
Shaft angle calculation
116124
*/
117-
float Encoder::getSensorAngle(){
125+
angle_type Encoder::getSensorAngle(){
126+
#ifdef INTEGER_ANGLE
127+
return angle_prev % steps_per_revolution;
128+
#else
118129
return _2PI * (pulse_counter) / ((float)cpr);
130+
#endif
119131
}
120132

121133

src/sensors/Encoder.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class Encoder: public Sensor{
6060

6161
// Abstract functions of the Sensor class implementation
6262
/** get current angle (rad) */
63-
float getSensorAngle() override;
63+
angle_type getSensorAngle() override;
6464
/** get current angular velocity (rad/s) */
6565
float getVelocity() override;
6666
virtual void update() override;

src/sensors/GenericSensor.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
- readCallback - pointer to the function which reads the sensor angle.
77
*/
88

9-
GenericSensor::GenericSensor(float (*readCallback)(), void (*initCallback)()){
9+
GenericSensor::GenericSensor(angle_type (*readCallback)(), void (*initCallback)()){
1010
// if function provided add it to the
1111
if(readCallback != nullptr) this->readCallback = readCallback;
1212
if(initCallback != nullptr) this->initCallback = initCallback;
@@ -21,6 +21,6 @@ void GenericSensor::init(){
2121
/*
2222
Shaft angle calculation
2323
*/
24-
float GenericSensor::getSensorAngle(){
24+
angle_type GenericSensor::getSensorAngle(){
2525
return this->readCallback();
2626
}

src/sensors/GenericSensor.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@ class GenericSensor: public Sensor{
1414
* @param readCallback pointer to the function reading the sensor angle
1515
* @param initCallback pointer to the function initialising the sensor
1616
*/
17-
GenericSensor(float (*readCallback)() = nullptr, void (*initCallback)() = nullptr);
17+
GenericSensor(angle_type (*readCallback)() = nullptr, void (*initCallback)() = nullptr);
1818

19-
float (*readCallback)() = nullptr; //!< function pointer to sensor reading
19+
angle_type (*readCallback)() = nullptr; //!< function pointer to sensor reading
2020
void (*initCallback)() = nullptr; //!< function pointer to sensor initialisation
2121

2222
void init() override;
2323

2424
// Abstract functions of the Sensor class implementation
2525
/** get current angle (rad) */
26-
float getSensorAngle() override;
26+
angle_type getSensorAngle() override;
2727

2828
};
2929

src/sensors/HallSensor.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ HallSensor::HallSensor(int _hallA, int _hallB, int _hallC, int _pp){
1515

1616
// hall has 6 segments per electrical revolution
1717
cpr = _pp * 6;
18+
#ifdef INTEGER_ANGLE
19+
steps_per_revolution = cpr;
20+
#endif
1821

1922
// extern pullup as default
2023
pullup = Pullup::USE_EXTERN;
@@ -108,8 +111,7 @@ void HallSensor::update() {
108111
long last_electric_rotations = electric_rotations;
109112
int8_t last_electric_sector = electric_sector;
110113
if (use_interrupt) interrupts();
111-
angle_prev = ((float)((last_electric_rotations * 6 + last_electric_sector) % cpr) / (float)cpr) * _2PI ;
112-
full_rotations = (int32_t)((last_electric_rotations * 6 + last_electric_sector) / cpr);
114+
setAngleContinuous((last_electric_rotations * 6 + last_electric_sector) % cpr);
113115
}
114116

115117

@@ -118,8 +120,12 @@ void HallSensor::update() {
118120
Shaft angle calculation
119121
TODO: numerical precision issue here if the electrical rotation overflows the angle will be lost
120122
*/
121-
float HallSensor::getSensorAngle() {
123+
angle_type HallSensor::getSensorAngle() {
124+
#ifdef INTEGER_ANGLE
125+
return electric_rotations * 6 + electric_sector;
126+
#else
122127
return ((float)(electric_rotations * 6 + electric_sector) / (float)cpr) * _2PI ;
128+
#endif
123129
}
124130

125131
/*

src/sensors/HallSensor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class HallSensor: public Sensor{
5757
/** Interrupt-safe update */
5858
void update() override;
5959
/** get current angle (rad) */
60-
float getSensorAngle() override;
60+
angle_type getSensorAngle() override;
6161
/** get current angular velocity (rad/s) */
6262
float getVelocity() override;
6363

0 commit comments

Comments
 (0)