libstp
Robotics library for Botball
Loading...
Searching...
No Matches
SimWorld.hpp
Go to the documentation of this file.
1#pragma once
2
6
7#include <array>
8#include <cstdint>
9#include <optional>
10#include <random>
11#include <string>
12#include <unordered_map>
13#include <vector>
14
15namespace libstp::sim
16{
20 enum class DrivetrainKind : uint8_t
21 {
24 };
25
30 {
32
33 // Differential drive: left + right wheel ports.
34 uint8_t leftPort{0};
35 uint8_t rightPort{1};
36 bool leftInverted{false};
37 bool rightInverted{false};
38
39 // Mecanum drive: front-left, front-right, back-left, back-right ports.
40 uint8_t flPort{0};
41 uint8_t frPort{1};
42 uint8_t blPort{2};
43 uint8_t brPort{3};
44 bool flInverted{false};
45 bool frInverted{false};
46 bool blInverted{false};
47 bool brInverted{false};
48
52
56
61 float ticksToRad{0.00436332313f};
62
68 float bemfSampleRateHz{200.0f};
69
74 float viscousDragCoeff{0.0f};
75
81
85 float bemfNoiseStddev{0.0f};
86 };
87
95 {
96 public:
97 SimWorld() = default;
98
99 void configure(const RobotConfig& robot, const SimMotorMap& motors);
101 const WorldMap& map() const noexcept { return m_map; }
102
107 int currentLayer() const noexcept { return m_currentLayer; }
108 void setCurrentLayer(int idx) noexcept { m_currentLayer = idx; }
109
112 void attachLineSensor(uint8_t analogPort, float forwardCm, float strafeCm,
113 std::string name = {});
114
118 void attachDistanceSensor(uint8_t analogPort, float forwardCm, float strafeCm,
119 float mountAngleRad = 0.0f,
120 float maxRangeCm = 100.0f,
121 std::string name = {});
122
124 void detachSensor(uint8_t analogPort);
125
129 std::optional<uint16_t> readAnalog(uint8_t analogPort) const;
130
131 void setPose(const Pose2D& pose);
132 Pose2D pose() const noexcept { return m_pose; }
133 float yawRateRadS() const noexcept { return m_yawRateRadS; }
134
145 Pose2D odometryPose() const noexcept { return m_odoPose; }
146
157 {
158 float vxBias{1.0f};
159 float vyBias{1.0f};
160 float yawBias{1.0f};
161 };
162 void setOdometryDrift(float vxBias, float vyBias, float yawBias) noexcept
163 {
164 m_odoDrift = OdometryDrift{vxBias, vyBias, yawBias};
165 }
166 OdometryDrift odometryDrift() const noexcept { return m_odoDrift; }
167
170 void setMotorCommand(uint8_t port, int signedPercent);
171
175 void setMotorVelocityCommand(uint8_t port, int bemfUnits);
176
180 int motorBemf(uint8_t port) const;
181
184 int motorPositionTicks(uint8_t port) const;
185
188 void tick(float dt);
189
192
196 bool isSensorOnBlackLine(const SensorMount& mount) const;
197
201 float wheelOmegaLeft() const noexcept { return m_wheelOmega[kLeft]; }
202 float wheelOmegaRight() const noexcept { return m_wheelOmega[kRight]; }
203
206 float wheelOmega(std::size_t idx) const noexcept
207 {
208 return idx < m_wheelOmega.size() ? m_wheelOmega[idx] : 0.0f;
209 }
210
211 const std::vector<Pose2D>& trace() const noexcept { return m_trace; }
212 void clearTrace() { m_trace.clear(); }
213
214 private:
215 static constexpr std::size_t kNumMotorPorts = 4;
216 static constexpr std::size_t kMaxWheels = 4;
217 static constexpr std::size_t kLeft = 0;
218 static constexpr std::size_t kRight = 1;
219 // Mecanum-specific aliases for clarity.
220 static constexpr std::size_t kFL = 0;
221 static constexpr std::size_t kFR = 1;
222 static constexpr std::size_t kBL = 2;
223 static constexpr std::size_t kBR = 3;
224
225 enum class SensorKind : uint8_t { Line, Distance };
226
227 struct SensorEntry
228 {
229 SensorKind kind{SensorKind::Line};
230 SensorMount mount{};
231 float mountAngleRad{0.0f}; // distance sensor aim direction in robot frame
232 float maxRangeCm{100.0f};
233 };
234
235 RobotConfig m_robot{};
236 SimMotorMap m_motors{};
237 WorldMap m_map{};
238 int m_currentLayer{0}; // index of the plane the robot is on (ground = 0)
239 Pose2D m_pose{};
240 Pose2D m_odoPose{}; // dead-reckoned controller-facing odometry (see odometryPose())
241 OdometryDrift m_odoDrift{}; // sim-only per-axis odometry bias (see setOdometryDrift)
242
243 std::array<float, kNumMotorPorts> m_motorTargetOmega{}; // rad/s, signed
244 std::array<float, kMaxWheels> m_wheelOmega{}; // rad/s
245 std::array<float, kMaxWheels> m_wheelAngleRad{}; // rad, signed
246 float m_yawRateRadS{0.0f};
247 std::vector<Pose2D> m_trace;
248 std::unordered_map<uint8_t, SensorEntry> m_sensors;
249
252 void applyLayerTransitions(const Pose2D& prev, const Pose2D& cur);
253
254 float targetOmega(std::size_t wheelIndex) const;
255 uint16_t sampleLineSensor(const SensorEntry&) const;
256 uint16_t sampleDistanceSensor(const SensorEntry&) const;
257 std::size_t portToWheelIndex(uint8_t port) const;
258 bool wheelInverted(std::size_t wheelIndex) const;
259 uint8_t wheelPort(std::size_t wheelIndex) const;
260 std::size_t activeWheelCount() const noexcept;
261
264 mutable std::mt19937 m_rng{42};
265 mutable std::normal_distribution<float> m_noiseDist{0.0f, 1.0f};
266 };
267}
Pose2D odometryPose() const noexcept
Definition SimWorld.hpp:145
float wheelOmega(std::size_t idx) const noexcept
Definition SimWorld.hpp:206
bool isSensorOnBlackLine(const SensorMount &mount) const
const std::vector< Pose2D > & trace() const noexcept
Definition SimWorld.hpp:211
float wheelOmegaRight() const noexcept
Definition SimWorld.hpp:202
int currentLayer() const noexcept
Definition SimWorld.hpp:107
OdometryDrift odometryDrift() const noexcept
Definition SimWorld.hpp:166
Pose2D pose() const noexcept
Definition SimWorld.hpp:132
void setMap(WorldMap map)
std::optional< uint16_t > readAnalog(uint8_t analogPort) const
const WorldMap & map() const noexcept
Definition SimWorld.hpp:101
void setPose(const Pose2D &pose)
void detachSensor(uint8_t analogPort)
Detach any sensor bound to analogPort.
Vec2 sensorWorldPosition(const SensorMount &mount) const
World-frame position of a sensor mount attached to this robot.
int motorPositionTicks(uint8_t port) const
void setCurrentLayer(int idx) noexcept
Definition SimWorld.hpp:108
void setOdometryDrift(float vxBias, float vyBias, float yawBias) noexcept
Definition SimWorld.hpp:162
void configure(const RobotConfig &robot, const SimMotorMap &motors)
void attachLineSensor(uint8_t analogPort, float forwardCm, float strafeCm, std::string name={})
void setMotorCommand(uint8_t port, int signedPercent)
float yawRateRadS() const noexcept
Definition SimWorld.hpp:133
int motorBemf(uint8_t port) const
void tick(float dt)
void setMotorVelocityCommand(uint8_t port, int bemfUnits)
float wheelOmegaLeft() const noexcept
Definition SimWorld.hpp:201
void attachDistanceSensor(uint8_t analogPort, float forwardCm, float strafeCm, float mountAngleRad=0.0f, float maxRangeCm=100.0f, std::string name={})
libstp::map::WorldMap WorldMap
Definition WorldMap.hpp:12
@ Differential
Two wheels (left/right). Default for legacy configs.
Definition SimWorld.hpp:22
@ Mecanum
Four wheels (FL/FR/BL/BR) with 45° rollers.
Definition SimWorld.hpp:23
DrivetrainKind kind
Definition SimWorld.hpp:31