1use crate::pid::Pid;
2use crate::pupdevices::GyroSensor;
3use crate::robotics::GyroController;
4use crate::Ev3Error;
5use crate::{parameters::Stop, pupdevices::Motor, Ev3Result};
6use fixed::traits::{LossyInto, ToFixed};
7use fixed::types::I32F32;
8use scopeguard::defer;
9use std::cell::Cell;
10use std::time::Duration;
11use tokio::time::interval;
12
13pub struct DriveBase<'a> {
49 left_motor: &'a Motor,
50 right_motor: &'a Motor,
51 left_start_angle: i32,
52 right_start_angle: i32,
53 min_speed: I32F32,
54 wheel_diameter: I32F32,
55 axle_track: I32F32,
56 straight_speed: Cell<I32F32>,
57 turn_speed: Cell<I32F32>,
58 prev_encoder_heading: Cell<I32F32>,
59 distance_pid: Pid,
60 heading_pid: Pid,
61 distance_target: Cell<I32F32>,
62 heading_target: Cell<I32F32>,
63 distance_tolerance: Cell<I32F32>,
64 heading_tolerance: Cell<I32F32>,
65 using_gyros: Cell<bool>,
66 gyros: Option<GyroController<'a>>,
67}
68
69impl<'a> DriveBase<'a> {
70 pub async fn new<Number>(
76 left_motor: &'a Motor,
77 right_motor: &'a Motor,
78 wheel_diameter: Number,
79 axle_track: Number,
80 ) -> Ev3Result<Self>
81 where
82 Number: ToFixed,
83 {
84 left_motor.set_ramp_up_setpoint(2000)?;
85 right_motor.set_ramp_up_setpoint(2000)?;
86
87 left_motor.set_ramp_down_setpoint(1800)?;
88 right_motor.set_ramp_down_setpoint(1800)?;
89
90 Ok(Self {
91 left_motor,
92 right_motor,
93 left_start_angle: left_motor.angle().await?,
94 right_start_angle: right_motor.angle().await?,
95 min_speed: I32F32::from_num(100),
96 wheel_diameter: I32F32::from_num(wheel_diameter),
97 axle_track: I32F32::from_num(axle_track),
98 straight_speed: Cell::new(I32F32::from_num(500)),
99 turn_speed: Cell::new(I32F32::from_num(550)),
100 prev_encoder_heading: Cell::new(I32F32::ZERO),
101 distance_pid: Pid::new(10, 0, 8, 0, 0),
102 heading_pid: Pid::new(10, 0, 5, 0, 0),
103 distance_target: Cell::new(I32F32::ZERO),
104 heading_target: Cell::new(I32F32::ZERO),
105 distance_tolerance: Cell::new(I32F32::from_num(4)),
106 heading_tolerance: Cell::new(I32F32::from_num(0.75)),
107 using_gyros: Cell::new(false),
108 gyros: None,
109 })
110 }
111
112 pub async fn with_gyro<'b>(mut self, gyro_sensor: &'b GyroSensor) -> Ev3Result<Self>
142 where
143 'b: 'a,
144 {
145 self.gyros = Some(GyroController::new(vec![gyro_sensor]).await?);
146 Ok(self)
147 }
148
149 pub async fn with_gyros<'b>(mut self, gyro_sensors: Vec<&'b GyroSensor>) -> Ev3Result<Self>
180 where
181 'b: 'a,
182 {
183 self.gyros = Some(GyroController::new(gyro_sensors).await?);
184 Ok(self)
185 }
186
187 pub fn use_gyro(&self, use_gyro: bool) -> Ev3Result<()> {
191 if use_gyro && self.gyros.is_none() {
192 return Err(Ev3Error::NoSensorProvided);
193 }
194 self.using_gyros.set(use_gyro);
195 Ok(())
196 }
197
198 pub fn set_straight_speed<Number>(&self, straight_speed: Number)
202 where
203 Number: ToFixed,
204 {
205 self.straight_speed.set(I32F32::from_num(straight_speed));
206 }
207
208 pub fn set_turn_speed<Number>(&self, turn_speed: Number)
212 where
213 Number: ToFixed,
214 {
215 self.turn_speed.set(I32F32::from_num(turn_speed));
216 }
217
218 pub fn set_ramp_up_setpoint(&self, sp: u32) -> Ev3Result<()> {
226 self.left_motor.set_ramp_up_setpoint(sp)?;
227 self.right_motor.set_ramp_up_setpoint(sp)
228 }
229
230 pub fn set_ramp_down_setpoint(&self, sp: u32) -> Ev3Result<()> {
238 self.left_motor.set_ramp_down_setpoint(sp)?;
239 self.right_motor.set_ramp_down_setpoint(sp)
240 }
241 pub fn set_stop_action(&self, action: Stop) -> Ev3Result<()> {
245 self.left_motor.set_stop_action(action)?;
246 self.right_motor.set_stop_action(action)
247 }
248
249 pub fn distance_pid_settings<Number>(
253 &self,
254 kp: Number,
255 ki: Number,
256 kd: Number,
257 integral_deadzone: Number,
258 integral_rate: Number,
259 ) where
260 Number: ToFixed,
261 {
262 self.distance_pid
263 .settings(kp, ki, kd, integral_deadzone, integral_rate);
264 }
265
266 pub fn heading_pid_settings<Number>(
270 &self,
271 kp: Number,
272 ki: Number,
273 kd: Number,
274 integral_deadzone: Number,
275 integral_rate: Number,
276 ) where
277 Number: ToFixed,
278 {
279 self.heading_pid
280 .settings(kp, ki, kd, integral_deadzone, integral_rate);
281 }
282
283 pub fn stop(&self) -> Ev3Result<()> {
289 self.left_motor.stop_prev_action()?;
290 self.right_motor.stop_prev_action()
291 }
292
293 async fn drive_relative(&self, distance_mm: I32F32, angle_deg: I32F32) -> Ev3Result<()> {
294 defer! {
295 _ = self.stop()
296 }
297
298 self.distance_pid.reset();
299 self.heading_pid.reset();
300
301 let target_distance = self.distance_target.get() + distance_mm;
302 let target_heading = self.heading_target.get() + angle_deg;
303
304 self.distance_target.set(target_distance);
305 self.heading_target.set(target_heading);
306
307 let mut timer = interval(Duration::from_millis(5));
308
309 timer.tick().await;
311
312 let straight_speed = self.straight_speed.get();
313 let turn_speed = self.turn_speed.get();
314
315 loop {
316 let left_angle =
317 I32F32::from_num(self.left_motor.angle().await? - self.left_start_angle);
318 let right_angle =
319 I32F32::from_num(self.right_motor.angle().await? - self.right_start_angle);
320 let current_distance = self.encoders_to_distance(left_angle, right_angle);
321 let current_heading = if self.using_gyros.get()
322 && let Some(ref gyro) = self.gyros
323 {
324 let encoders = self.encoders_to_heading().await?;
325 I32F32::from_num(gyro.heading().await?) * I32F32::from_num(0.9)
326 + encoders * I32F32::from_num(0.1)
327 } else {
328 self.encoders_to_heading().await?
329 };
330
331 let distance_error = target_distance - current_distance;
332 let heading_error = target_heading - current_heading;
333
334 if distance_error.abs() < self.distance_tolerance.get()
335 && heading_error.abs() < self.heading_tolerance.get()
336 {
337 break;
338 }
339
340 let dive_effort = self.distance_pid.next(distance_error);
341 let turn_effort = -self.heading_pid.next(heading_error);
342
343 let drive_speed_out = dive_effort * straight_speed;
344 let turn_speed_out = turn_effort * turn_speed;
345
346 let left_speed = (drive_speed_out - turn_speed_out)
347 .clamp(-self.right_motor.max_speed, self.left_motor.max_speed);
348
349 let right_speed = (drive_speed_out + turn_speed_out)
350 .clamp(-self.left_motor.max_speed, self.right_motor.max_speed);
351
352 self.left_motor.run(
353 (if left_speed.abs() < self.min_speed {
354 self.min_speed * left_speed.signum()
355 } else {
356 left_speed
357 })
358 .lossy_into(),
359 )?;
360 self.right_motor.run(
361 (if right_speed.abs() < self.min_speed {
362 self.min_speed * right_speed.signum()
363 } else {
364 right_speed
365 })
366 .lossy_into(),
367 )?;
368
369 timer.tick().await;
370 }
371
372 Ok(())
373 }
374
375 pub async fn straight<Number>(&self, distance: Number) -> Ev3Result<()>
377 where
378 Number: ToFixed,
379 {
380 self.drive_relative(I32F32::from_num(distance), I32F32::from_num(0))
381 .await
382 }
383
384 pub async fn turn<Number>(&self, angle: Number) -> Ev3Result<()>
386 where
387 Number: ToFixed,
388 {
389 self.drive_relative(I32F32::from_num(0), I32F32::from_num(angle))
390 .await
391 }
392
393 pub async fn curve<Number>(&self, radius: Number, angle: Number) -> Ev3Result<()>
395 where
396 Number: ToFixed,
397 {
398 let fixed_angle = I32F32::from_num(angle);
399
400 let angle_rad = fixed_angle * I32F32::PI / 180;
401 let arc_length = I32F32::from_num(radius).abs() * I32F32::from_num(angle_rad).abs();
402
403 self.drive_relative(arc_length, I32F32::from_num(fixed_angle))
404 .await
405 }
406
407 pub async fn veer<Number>(&self, radius: Number, distance: Number) -> Ev3Result<()>
409 where
410 Number: ToFixed,
411 {
412 let fixed_distance = I32F32::from_num(distance);
413
414 let angle_rad = fixed_distance / I32F32::from_num(radius);
415 let angle_deg = angle_rad * 180 / I32F32::PI;
416
417 self.drive_relative(fixed_distance, I32F32::from_num(angle_deg))
418 .await
419 }
420
421 pub async fn find_calibrated_axle_track<Number>(
429 &mut self,
430 margin_of_error: Number,
431 ) -> Ev3Result<I32F32>
432 where
433 Number: ToFixed,
434 {
435 self.use_gyro(true)?;
436 let fixed_estimate = I32F32::from_num(self.axle_track);
437 let fixed_margin_of_error = I32F32::from_num(margin_of_error);
438 let resphi = I32F32::FRAC_1_PHI;
439
440 let mut a = fixed_estimate - fixed_margin_of_error;
441 let mut b = fixed_estimate + fixed_margin_of_error;
442 let tolerance = I32F32::from_num(0.5);
443
444 let mut x1 = a + resphi * (b - a);
446 let mut x2 = b - resphi * (b - a);
447
448 let mut f1 = self.test_axle_track(x1).await?;
449 let mut f2 = self.test_axle_track(x2).await?;
450
451 while (b - a).abs() > tolerance {
453 if f1 < f2 {
454 a = x2;
456 x2 = x1;
457 f2 = f1;
458 x1 = a + resphi * (b - a);
459 f1 = self.test_axle_track(x1).await?;
460 } else {
461 b = x1;
463 x1 = x2;
464 f1 = f2;
465 x2 = b - resphi * (b - a);
466 f2 = self.test_axle_track(x2).await?;
467 }
468 }
469
470 let best = (a + b) / I32F32::from_num(2);
471 println!("Best axle track: {}", best);
472
473 Ok(best)
474 }
475
476 async fn test_axle_track(&mut self, candidate: I32F32) -> Ev3Result<I32F32> {
478 println!("testing {}", candidate);
479 self.axle_track = candidate;
480
481 if let Some(ref gyros) = self.gyros {
483 let start_encoder_heading = self.encoders_to_heading().await?;
484 gyros.reset().await?;
485 self.turn(90).await?;
487
488 let gyro_turned = gyros.heading().await?;
490 let encoder_turned = self.encoders_to_heading().await? - start_encoder_heading;
491
492 let error = (gyro_turned - encoder_turned).abs();
493 println!("Error: {}", error);
494
495 Ok(error)
496 } else {
497 Err(Ev3Error::NoSensorProvided)
498 }
499 }
500
501 fn encoders_to_distance(&self, left_deg: I32F32, right_deg: I32F32) -> I32F32 {
503 let wheel_circ = I32F32::PI * self.wheel_diameter;
504 let left_mm = wheel_circ * left_deg / 360;
505 let right_mm = wheel_circ * right_deg / 360;
506 (left_mm + right_mm) / 2
507 }
508
509 async fn encoders_to_heading(&self) -> Ev3Result<I32F32> {
511 let left_deg = I32F32::from_num(self.left_motor.angle().await? - self.left_start_angle);
512 let right_deg = I32F32::from_num(self.right_motor.angle().await? - self.right_start_angle);
513
514 let wheel_circ = I32F32::PI * self.wheel_diameter;
515 let left_mm = wheel_circ * left_deg / 360;
516 let right_mm = wheel_circ * right_deg / 360;
517 let arc_diff = left_mm - right_mm;
518 let turn_rad = arc_diff / self.axle_track;
519 let raw_heading = turn_rad * 180 / I32F32::PI;
520
521 let alpha = I32F32::from_num(0.7);
522 let filtered =
523 alpha * raw_heading + (I32F32::from_num(1) - alpha) * self.prev_encoder_heading.get();
524 self.prev_encoder_heading.set(filtered);
525 Ok(filtered)
526 }
527}