4#include <sys/_intsup.h>
6#include "pros/abstract_motor.hpp"
7#include "pros/motors.h"
11Chassis::Chassis(lemlib::Drivetrain drivetrain,
12 lemlib::ControllerSettings lateralSettings,
13 lemlib::ControllerSettings angularSettings,
14 lemlib::OdomSensors sensors,
15 lemlib::DriveCurve* throttleCurve,
16 lemlib::DriveCurve* steerCurve)
17 : lemlib::Chassis(drivetrain, lateralSettings, angularSettings, sensors, throttleCurve, steerCurve),
18 drivetrain(drivetrain) {}
20Chassis::WheelPowers Chassis::desaturate(
float lateral,
float angular,
float maxSpeed) {
21 float left = lateral + angular;
22 float right = lateral - angular;
23 float maxMag = std::max(std::abs(left), std::abs(right));
24 if (maxMag > maxSpeed) {
25 left = (left / maxMag) * maxSpeed;
26 right = (right / maxMag) * maxSpeed;
31void Chassis::moveToPointRamsete(
float target_x,
float target_y,
int timeout_msec,
const VelocityControllerConfig &
config, MoveToPointParams params,
bool async) {
33 this->requestMotionStart();
34 this->distTraveled = 0;
36 auto movement_logic = [=,
this]()
mutable {
48 lemlib::PID angularPID(11, 0.001, 0);
49 lemlib::PID lateralPID(5, 0, 0);
50 lemlib::ExitCondition lateral(0.08, 100);
51 lemlib::ExitCondition longitudinal(0.08, 100);
53 float lateralGain = 0;
55 auto sign = [](
float x) {
return (x > 0) ? 1.0f : ((x < 0) ? -1.0f : 0.0f); };
56 auto sinc = [](
float x) {
return (std::abs(x) < 1e-5) ? 1.0f : std::sin(x) / x; };
59 bool is_settling =
false;
61 lemlib::Pose lastPose = this->getPose(
true,
true);
62 uint32_t startTime = pros::millis();
63 while ((pros::millis() - startTime <
static_cast<uint32_t
>(timeout_msec)) && (!lateral.getExit() || !longitudinal.getExit())) {
65 if (this->motionQueued) {
69 lemlib::Pose currentPoseRaw = this->getPose(
true,
true);
70 this->distTraveled += lastPose.distance(currentPoseRaw);
71 lastPose = currentPoseRaw;
73 lemlib::Pose currentPose = currentPoseRaw;
77 float d = std::hypot(target_x - currentPose.x, target_y - currentPose.y);
78 float dx = target_x - currentPose.x;
79 float dy = target_y - currentPose.y;
81 if (!params.forwards) {
86 float theta = currentPose.theta;
87 float localErrorX = std::cos(theta) * dx + std::sin(theta) * dy;
88 float localErrorY = -std::sin(theta) * dx + std::cos(theta) * dy;
90 float angularError = std::atan2(localErrorY, localErrorX);
91 float cosineScaling = std::cos(angularError);
100 cosineScaling = 1.0f;
101 driveError = std::cos(theta) * dx + std::sin(theta) * dy;
103 driveError = d * sign(cosineScaling);
106 if (params.minSpeed > 0 && (d < earlyExit_m || driveError < 0)) {
110 lateral.update(localErrorY);
111 longitudinal.update(localErrorX);
113 float angularOutput = angularPID.update(angularError);
114 float driveOutput = lateralPID.update(driveError);
116 driveOutput = std::clamp(driveOutput, -params.maxSpeed, params.maxSpeed);
117 driveOutput = driveOutput * std::abs(cosineScaling);
122 angularOutput += driveOutput * lateralGain * localErrorY * sinc(angularError);
125 if (!is_settling && params.minSpeed > 0 && std::abs(driveOutput) < params.minSpeed) {
126 driveOutput = params.minSpeed * sign(driveOutput);
127 if (driveOutput == 0) driveOutput = params.minSpeed;
130 angularOutput = std::clamp(angularOutput, -params.maxTurnSpeed, params.maxTurnSpeed);
132 if (!params.forwards) {
133 driveOutput = -driveOutput;
136 float leftVel = drivetrain.leftMotors->get_actual_velocity() *
rpm_to_mps_factor;
137 float rightVel = drivetrain.rightMotors->get_actual_velocity() *
rpm_to_mps_factor;
139 DrivetrainVoltages outputVoltages =
controller.update(driveOutput, angularOutput, leftVel, rightVel);
141 drivetrain.leftMotors->move_voltage(outputVoltages.leftVoltage * 1000);
142 drivetrain.rightMotors->move_voltage(outputVoltages.rightVoltage * 1000);
147 if (params.minSpeed == 0) {
148 drivetrain.leftMotors->move_voltage(0);
149 drivetrain.rightMotors->move_voltage(0);
152 this->motionRunning =
false;
156 pros::Task task(movement_logic);
162void Chassis::moveToPoseRamsete(
float targetX,
float targetY,
float targetTheta,
int timeout_msec,
const VelocityControllerConfig &
config, MoveToPoseParams params,
bool async) {
164 this->requestMotionStart();
165 this->distTraveled = 0;
167 auto movement_logic = [=,
this]()
mutable {
180 lemlib::PID angularPID(7.5, 0.001, 26.5);
181 lemlib::PID lateralPID(5.5 , 0.001 , 27);
183 lemlib::ExitCondition lateralExit(0.08, 100);
184 lemlib::ExitCondition longitudinalExit(0.08, 100);
186 auto sign_func = [](
float x) {
return (x > 0) ? 1.0f : ((x < 0) ? -1.0f : 0.0f); };
187 auto sinc = [](
float x) {
return (std::abs(x) < 1e-5) ? 1.0f : std::sin(x) / x; };
191 float targetThetaRad = targetTheta * (M_PI / 180.0f);
192 float end_unit_x = std::sin(targetThetaRad);
193 float end_unit_y = std::cos(targetThetaRad);
194 float dir_sign = params.forwards ? 1.0f : -1.0f;
196 bool is_settling =
false;
198 lemlib::Pose lastPose = this->getPose(
true,
true);
199 uint32_t startTime = pros::millis();
201 while ((pros::millis() - startTime <
static_cast<uint32_t
>(timeout_msec)) && (!lateralExit.getExit() || !longitudinalExit.getExit())) {
203 if (this->motionQueued) {
207 lemlib::Pose currentPoseRaw = this->getPose(
true,
true);
208 this->distTraveled += lastPose.distance(currentPoseRaw);
209 lastPose = currentPoseRaw;
211 lemlib::Pose currentPose = currentPoseRaw;
214 float theta = currentPose.theta;
216 float d = std::hypot(targetX - currentPose.x, targetY - currentPose.y);
218 float true_dx = targetX - currentPose.x;
219 float true_dy = targetY - currentPose.y;
220 if (!params.forwards) {
224 float true_localErrorX = std::cos(theta) * true_dx + std::sin(theta) * true_dy;
225 float true_localErrorY = -std::sin(theta) * true_dx + std::cos(theta) * true_dy;
227 float rx = currentPose.x - targetX;
228 float ry = currentPose.y - targetY;
229 float along_track = rx * end_unit_x + ry * end_unit_y;
230 float dynamic_lookahead = d * params.lead;
231 float lookahead_m = std::max(dynamic_lookahead, 0.35f);
232 float ghost_along_track = along_track + (lookahead_m * dir_sign);
234 float carrot_x = targetX + ghost_along_track * end_unit_x;
235 float carrot_y = targetY + ghost_along_track * end_unit_y;
237 float dx = carrot_x - currentPose.x;
238 float dy = carrot_y - currentPose.y;
239 if (!params.forwards) {
244 float localErrorX = std::cos(theta) * dx + std::sin(theta) * dy;
245 float localErrorY = -std::sin(theta) * dx + std::cos(theta) * dy;
247 float heading_error_rad = std::atan2(localErrorY, localErrorX);
248 float cosine_scale = std::cos(heading_error_rad);
250 float error_norm_sq = localErrorX * localErrorX + localErrorY * localErrorY;
251 float driveError = std::sqrt((error_norm_sq + 2.0f * d * d) / 3.0f) * sign_func(cosine_scale);
252 float turnError = heading_error_rad;
254 if (d < std::max(settleRadius_m, 0.05f)) {
259 float targetThetaMath = M_PI_2 - targetThetaRad;
260 float final_error = targetThetaMath - theta;
261 final_error = std::remainder(final_error, 2.0 * M_PI);
263 turnError = final_error;
264 driveError = true_localErrorX;
268 if (params.earlyExitRange > 0 && (d < earlyExit_m || driveError < 0)) {
272 lateralExit.update(true_localErrorY);
273 longitudinalExit.update(true_localErrorX);
275 float driveOutput = lateralPID.update(driveError);
276 float turnOutput = angularPID.update(turnError);
278 driveOutput = std::clamp(driveOutput, -params.maxSpeed, params.maxSpeed);
281 float steeringVelocity = std::max(std::abs(driveOutput), 0.2f);
282 turnOutput += steeringVelocity * params.k_lat * localErrorY * sinc(heading_error_rad);
285 driveOutput = std::abs(cosine_scale) * driveOutput;
287 if (!is_settling && params.minSpeed > 0 && std::abs(driveOutput) < params.minSpeed) {
288 driveOutput = params.minSpeed * sign_func(driveOutput);
289 if (driveOutput == 0) driveOutput = params.minSpeed;
292 if (!params.forwards) {
293 driveOutput = -driveOutput;
296 turnOutput = std::clamp(turnOutput, -params.maxTurnSpeed, params.maxTurnSpeed);
298 float leftVel = drivetrain.leftMotors->get_actual_velocity() *
rpm_to_mps_factor;
299 float rightVel = drivetrain.rightMotors->get_actual_velocity() *
rpm_to_mps_factor;
301 DrivetrainVoltages outputVoltages =
controller.update(driveOutput, turnOutput, leftVel, rightVel);
303 drivetrain.leftMotors->move_voltage(outputVoltages.leftVoltage * 1000);
304 drivetrain.rightMotors->move_voltage(outputVoltages.rightVoltage * 1000);
309 if (params.minSpeed == 0) {
310 drivetrain.leftMotors->move_voltage(0);
311 drivetrain.rightMotors->move_voltage(0);
314 this->motionRunning =
false;
318 pros::Task task(movement_logic);
324void Chassis::tank(
float lin_vel,
float ang_vel,
const VelocityControllerConfig &
config,
unsigned int time)
333 u_int32_t starting_time = pros::millis();
335 while(pros::millis() - starting_time < time)
337 float leftVel = drivetrain.leftMotors->get_actual_velocity() * 0.00324173f;
338 float rightVel = drivetrain.rightMotors->get_actual_velocity() * 0.00324173f;
340 DrivetrainVoltages outputVoltages =
controller.update(lin_vel, ang_vel, leftVel, rightVel);
342 drivetrain.leftMotors->move_voltage(outputVoltages.leftVoltage * 1000);
343 drivetrain.rightMotors->move_voltage(outputVoltages.rightVoltage * 1000);
348 drivetrain.leftMotors->brake();
349 drivetrain.rightMotors->brake();
352void Chassis::brake(pros::MotorBrake brake_mode)
354 drivetrain.rightMotors->set_brake_mode(brake_mode);
355 drivetrain.leftMotors->set_brake_mode(brake_mode);
356 drivetrain.rightMotors->brake();
357 drivetrain.leftMotors->brake();
360void Chassis::tank(
int left,
int right)
362 drivetrain.rightMotors->move(right);
363 drivetrain.leftMotors->move(left);
366bool Chassis::collision_montitoring(
unsigned int time)
372bool Chassis::detect_collision()
374 double right_target = std::abs(drivetrain.rightMotors->get_target_velocity());
375 double left_target = std::abs(drivetrain.leftMotors->get_target_velocity());
376 double right_actual = std::abs(drivetrain.rightMotors->get_actual_velocity());
377 double left_actual = std::abs(drivetrain.leftMotors->get_actual_velocity());
378 double right_eff = drivetrain.rightMotors->get_efficiency();
379 double left_eff = drivetrain.leftMotors->get_efficiency();
381 const double TARGET_THRESHOLD = 50.0;
382 const double VELOCITY_THRESHOLD = 5.0;
383 const double EFFICIENCY_THRESHOLD = 20.0;
384 bool right_commanded = right_target > TARGET_THRESHOLD;
385 bool left_commanded = left_target > TARGET_THRESHOLD;
386 bool right_struggling = (right_actual < VELOCITY_THRESHOLD) || (right_eff < EFFICIENCY_THRESHOLD);
387 bool left_struggling = (left_actual < VELOCITY_THRESHOLD) || (left_eff < EFFICIENCY_THRESHOLD);
389 bool right_colliding = right_commanded && right_struggling;
390 bool left_colliding = left_commanded && left_struggling;
391 static uint32_t stall_start_time = 0;
393 if (right_colliding || left_colliding)
395 if (stall_start_time == 0)
397 stall_start_time = pros::millis();
399 if (pros::millis() - stall_start_time > 250)
406 stall_start_time = 0;
const VelocityControllerConfig config
pros::Controller controller(pros::E_CONTROLLER_MASTER)
static constexpr float INCH_TO_METER
static constexpr float rpm_to_mps_factor