TinyRobotics
Loading...
Searching...
No Matches
SpeedFromThrottle.h
1#pragma once
2
3#include <vector>
4
5#include "TinyRobotics/communication/MessageHandler.h"
6#include "TinyRobotics/odometry/ISpeedSource.h"
7#include "TinyRobotics/units/Speed.h"
8
9namespace tinyrobotics {
10
11/**
12 * @class SpeedFromThrottle
13 * @ingroup odometry
14 * @brief Estimates vehicle speed from throttle percentage using calibration
15 * data.
16 *
17 * This class provides a flexible, piecewise linear mapping from throttle
18 * percentage (e.g., -100% to 100%) to speed (in meters per second), based on
19 * user-provided calibration points. It is useful for systems where direct speed
20 * measurement is unavailable or unreliable, and a throttle-to-speed
21 * relationship can be established empirically. The mapping can be linear or
22 * non-linear, depending on the calibration data.
23 *
24 * **Features:**
25 * - By default, 0% throttle maps to 0 m/s, 100% throttle maps to maxSpeedMps,
26 * and -100% throttle maps to -maxSpeedMps.
27 * - Additional calibration points can be added with
28 * addSpeedCalibration(throttle, speed), allowing for non-linear mappings.
29 * - If a calibration point for a throttle value already exists, it is replaced;
30 * otherwise, it is inserted in order.
31 * - Supports both direct speed queries (getSpeed, getSpeedMPS) and integration
32 * with the TinyRobotics message system.
33 * - When used as a MessageHandler, it listens for Throttle messages and emits
34 * Speed messages via MessageSource.
35 * - Calibration data can be cleared and redefined at runtime.
36 *
37 * **Typical usage:**
38 * @code
39 * SpeedFromThrottle speedMap(2.0f); // 2 m/s at 100% throttle
40 * speedMap.addSpeedCalibration(50.0f, 1.0f); // 1 m/s at 50%
41 * float speed = speedMap.getSpeedMPS(75.0f); // Interpolated speed
42 * // As a message handler/source:
43 * bus.subscribe(speedMap); // Receives Throttle messages, emits Speed
44 * messages
45 * @endcode
46 *
47 * **Integration:**
48 * - Use as a standalone utility for throttle-to-speed conversion.
49 * - Or, connect to a MessageBus to automatically convert Throttle messages to
50 * Speed messages.
51 *
52 * @see addSpeedCalibration, getSpeed, getSpeedMPS, onMessage, clearCalibration
53 *
54 * @author Phil Schatzmann
55 */
56
57class SpeedFromThrottle : public MessageSource,
58 public MessageHandler,
59 public ISpeedSource {
60 public:
61 SpeedFromThrottle(float maxSpeedMps, uint8_t numMotors = 1) {
62 this->numMotors = numMotors;
63 this->maxSpeedMps = maxSpeedMps;
64 speedMps.resize(numMotors, 0.0f);
65 init();
66 }
67
68 SpeedFromThrottle(Speed maxSpeed, uint8_t numMotors = 1)
69 : SpeedFromThrottle(maxSpeed.getValue(SpeedUnit::MPS), numMotors) {}
70
71 void setMaxSpeed(Speed speed) {
72 this->maxSpeedMps = speed.getValue(SpeedUnit::MPS);
73 init();
74 }
75
76 /// Handle Throttle messages to update speed
77 bool onMessage(const Message<float>& msg) override {
78 if (msg.content == MessageContent::Throttle) {
79 setThrottlePercent(msg.value);
80 return true;
81 }
82 return false; // Not handled
83 }
84
85 /// Add or update a calibration point (throttlePercent, speedMps)
86 void addSpeedCalibration(float throttlePercent, float speedMps) {
87 auto it = calibrationData.begin();
88 while (it != calibrationData.end() && it->first < throttlePercent) {
89 ++it;
90 }
91 if (it != calibrationData.end() && it->first == throttlePercent) {
92 it->second = speedMps; // Replace existing
93 } else {
94 calibrationData.insert(
95 it, std::pair<float, float>(throttlePercent, speedMps));
96 }
97 }
98
99 /// Clear all calibration data
100 void clearCalibration() { calibrationData.clear(); }
101
102 /// Define the actual throttle value
103 void setThrottlePercent(float throttlePercent, uint8_t motor = 0) override {
104 assert(motor < numMotors);
105 speedMps[motor] = getSpeedMPS(throttlePercent);
106 sendSpeedMessage(motor);
107 }
108
109 /// Get the actual speed based on the last throttle value
110 Speed getSpeed(uint8_t motor = 0) const override {
111 assert(motor < numMotors);
112 return Speed(speedMps[motor], SpeedUnit::MPS);
113 }
114
115 Speed updateSpeed(uint32_t deltaTimeMs, uint8_t motor = 0) override {
116 assert(motor < numMotors);
117 return Speed(speedMps[motor], SpeedUnit::MPS);
118 }
119
120 size_t getMotorCount() const override { return numMotors; }
121
122 protected:
123 std::vector<std::pair<float, float>> calibrationData;
124 std::vector<float> speedMps;
125 uint8_t numMotors = 1;
126 float maxSpeedMps = 0.0f;
127
128 // Overload for multi-motor begin
129 bool init() {
130 calibrationData.clear();
131 addSpeedCalibration(0.0f, 0.0f); // 0% throttle = 0 m/s
132 addSpeedCalibration(100.0f, maxSpeedMps); // 100% throttle = max speed
134 maxSpeedMps); // -100% throttle = max reverse speed
135 return true;
136 }
137 void sendSpeedMessage(uint8_t motor = 0) {
138 assert(motor < numMotors);
139 Message<float> msg(MessageContent::Speed, speedMps[motor],
140 Unit::MetersPerSecond, MessageOrigin::System);
141 msg.origin_id = motor;
142 MessageSource::sendMessage(msg);
143 }
144
145 /// Get speed in meters per second by interpolating calibration data
146 float getSpeedMPS(float throttlePercent) const {
147 if (calibrationData.empty()) return 0.0f;
148 // Clamp below
149 if (throttlePercent <= calibrationData.front().first)
150 return calibrationData.front().second;
151 // Clamp above
152 if (throttlePercent >= calibrationData.back().first)
153 return calibrationData.back().second;
154 // Find interval
155 for (size_t i = 1; i < calibrationData.size(); ++i) {
156 float t0 = calibrationData[i - 1].first;
157 float t1 = calibrationData[i].first;
158 if (throttlePercent >= t0 && throttlePercent <= t1) {
159 float s0 = calibrationData[i - 1].second;
160 float s1 = calibrationData[i].second;
161 float alpha = (throttlePercent - t0) / (t1 - t0);
162 return s0 + alpha * (s1 - s0);
163 }
164 }
165 // Should not reach here
166 return 0.0f;
167 }
168};
169
170} // namespace tinyrobotics
Interface for speed sources.
Definition: ISpeedSource.h:9
Interface for handling messages in the TinyRobotics framework.
Definition: MessageHandler.h:18
Estimates vehicle speed from throttle percentage using calibration data.
Definition: SpeedFromThrottle.h:59
Speed getSpeed(uint8_t motor=0) const override
Get the actual speed based on the last throttle value.
Definition: SpeedFromThrottle.h:110
Speed updateSpeed(uint32_t deltaTimeMs, uint8_t motor=0) override
For sources with inertia, call this in your main loop with the elapsed time (in milliseconds) to upda...
Definition: SpeedFromThrottle.h:115
void addSpeedCalibration(float throttlePercent, float speedMps)
Add or update a calibration point (throttlePercent, speedMps)
Definition: SpeedFromThrottle.h:86
void setThrottlePercent(float throttlePercent, uint8_t motor=0) override
Define the actual throttle value.
Definition: SpeedFromThrottle.h:103
void clearCalibration()
Clear all calibration data.
Definition: SpeedFromThrottle.h:100
bool onMessage(const Message< float > &msg) override
Handle Throttle messages to update speed.
Definition: SpeedFromThrottle.h:77
float getSpeedMPS(float throttlePercent) const
Get speed in meters per second by interpolating calibration data.
Definition: SpeedFromThrottle.h:146
Represents a speed measurement with unit conversion support.
Definition: Speed.h:40
SpeedUnit
Supported speed units for conversion and representation.
Definition: Speed.h:10
Generic message structure for communication, parameterized by value type.
Definition: Message.h:72