1212#include <pbio/servo.h>
1313#include "pybricks/experimental/odometry.h"
1414
15- // THE LIFELINE: Bring in the Pybricks background task runner!
16- extern bool pbio_os_run_processes_once (void );
17-
18- // Hardware acceleration for the Spike Prime (FPU + RAM execution)
15+ // Hardware acceleration for the Spike Prime
1916#if defined(__ARM_ARCH_7M__ ) || defined(__ARM_ARCH_7EM__ )
2017#define ACCEL_RAM __attribute__((section(".data"), noinline))
2118#else
@@ -36,76 +33,106 @@ ACCEL_RAM static float fast_sin_internal(float theta) {
3633 return x * (0.99999906f + x2 * (-0.16665554f + x2 * (0.00831190f + x2 * -0.00018488f )));
3734}
3835
39- mp_obj_t calculate_odometry (int num_iters , float wheel_circ , float axle_track , mp_obj_t right_motor_obj , mp_obj_t left_motor_obj ) {
36+ ACCEL_RAM static float fast_cos_internal (float theta ) {
37+ return fast_sin_internal (theta + 1.57079633f );
38+ }
39+
40+ // ---------------------------------------------------------
41+ // BACKGROUND ODOMETRY STATE MACHINE
42+ // ---------------------------------------------------------
43+ volatile bool odom_running = false;
44+ volatile uint32_t last_odom_time_ms = 0 ;
4045
41- float deg_to_mm = wheel_circ * 0.0027777778f ;
42- float inv_axle_track = 1.0f / axle_track ;
46+ volatile float global_x = 0.0f ;
47+ volatile float global_y = 0.0f ;
48+ volatile float global_h = 0.0f ;
4349
44- float rx = 0.0f ;
45- float ry = 0.0f ;
46- float rh = 0.0f ;
50+ volatile int32_t last_left_angle = 0 ;
51+ volatile int32_t last_right_angle = 0 ;
4752
48- pbio_servo_t * srv_r = (( pb_type_Motor_obj_t * ) MP_OBJ_TO_PTR ( right_motor_obj )) -> srv ;
49- pbio_servo_t * srv_l = (( pb_type_Motor_obj_t * ) MP_OBJ_TO_PTR ( left_motor_obj )) -> srv ;
53+ float odom_deg_to_mm = 1.0f ;
54+ float odom_inv_track = 1.0f ;
5055
51- int32_t last_r , last_l , unused_rate ;
52- pbio_servo_get_state_user (srv_r , & last_r , & unused_rate );
53- pbio_servo_get_state_user (srv_l , & last_l , & unused_rate );
56+ pbio_servo_t * left_servo_ptr = NULL ;
57+ pbio_servo_t * right_servo_ptr = NULL ;
5458
55- uint32_t start_time = mp_hal_ticks_ms ();
59+ // This is the hook that will run continuously in the OS background
60+ void pb_background_odometry_update (void ) {
61+ if (!odom_running ) return ;
5662
57- for (int i = 0 ; i < num_iters ; i ++ ) {
58- int32_t cur_r , cur_l ;
59-
60- // 1. Instant Memory read
61- pbio_servo_get_state_user (srv_r , & cur_r , & unused_rate );
62- pbio_servo_get_state_user (srv_l , & cur_l , & unused_rate );
63+ // Throttle to 200Hz (5ms) to prevent starving the motor controllers
64+ uint32_t now = mp_hal_ticks_ms ();
65+ if (now - last_odom_time_ms < 5 ) return ;
66+ last_odom_time_ms = now ;
6367
64- // 2. THE BYPASS: Only run heavy FPU math if the wheels physically moved!
65- int32_t delta_r = cur_r - last_r ;
66- int32_t delta_l = cur_l - last_l ;
68+ int32_t cur_l , cur_r , unused_rate ;
69+ pbio_servo_get_state_user ( left_servo_ptr , & cur_l , & unused_rate ) ;
70+ pbio_servo_get_state_user ( right_servo_ptr , & cur_r , & unused_rate ) ;
6771
68- if (delta_r != 0 || delta_l != 0 ) {
69- float dR = (float )delta_r * deg_to_mm ;
70- float dL = (float )delta_l * deg_to_mm ;
71- float dD = (dR + dL ) * 0.5f ;
72- float dH = (dR - dL ) * inv_axle_track ;
72+ int32_t delta_l = cur_l - last_left_angle ;
73+ int32_t delta_r = cur_r - last_right_angle ;
7374
74- float avg_h = rh + (dH * 0.5f );
75- rx += dD * fast_sin_internal (avg_h + 1.5707963f ); // cos
76- ry += dD * fast_sin_internal (avg_h ); // sin
77- rh += dH ;
75+ if (delta_l != 0 || delta_r != 0 ) {
76+ float dL = (float )delta_l * odom_deg_to_mm ;
77+ float dR = (float )delta_r * odom_deg_to_mm ;
7878
79- last_r = cur_r ;
80- last_l = cur_l ;
81- }
79+ float dD = ( dR + dL ) * 0.5f ;
80+ float dH = ( dR - dL ) * odom_inv_track ;
81+ float avg_h = global_h + ( dH * 0.5f );
8282
83- // 3. THE LIFELINE: Pump the Pybricks OS so the motors actually drive
84- // (Bitwise & is a lightning-fast way to do % 256)
85- if (( i & 0xFF ) == 0 ) {
86- mp_handle_pending (true);
87- while ( pbio_os_run_processes_once ()) {}
88- }
83+ global_x += dD * fast_cos_internal ( avg_h );
84+ global_y += dD * fast_sin_internal ( avg_h );
85+ global_h += dH ;
86+
87+ last_left_angle = cur_l ;
88+ last_right_angle = cur_r ;
8989 }
90+ }
91+
92+ // Python API: experimental.start_odometry(left_motor, right_motor, circ, track, x, y, h)
93+ mp_obj_t experimental_start_odometry (size_t n_args , const mp_obj_t * args ) {
94+ left_servo_ptr = ((pb_type_Motor_obj_t * )MP_OBJ_TO_PTR (args [0 ]))-> srv ;
95+ right_servo_ptr = ((pb_type_Motor_obj_t * )MP_OBJ_TO_PTR (args [1 ]))-> srv ;
96+
97+ odom_deg_to_mm = mp_obj_get_float (args [2 ]) / 360.0f ;
98+ odom_inv_track = 1.0f / mp_obj_get_float (args [3 ]);
99+
100+ global_x = mp_obj_get_float (args [4 ]);
101+ global_y = mp_obj_get_float (args [5 ]);
102+ global_h = mp_obj_get_float (args [6 ]);
103+
104+ int32_t unused_rate ;
105+ pbio_servo_get_state_user (left_servo_ptr , (int32_t * )& last_left_angle , & unused_rate );
106+ pbio_servo_get_state_user (right_servo_ptr , (int32_t * )& last_right_angle , & unused_rate );
90107
91- uint32_t dur = mp_hal_ticks_ms () - start_time ;
108+ odom_running = true;
109+ return mp_const_none ;
110+ }
92111
93- mp_obj_t tuple [ 5 ] = {
94- mp_obj_new_float_from_f (( float ) dur * 0.001f ),
95- mp_obj_new_int ( num_iters ),
96- mp_obj_new_float_from_f (( float ) num_iters / (( float ) dur * 0.001f ) ),
97- mp_obj_new_float_from_f (rx ),
98- mp_obj_new_float_from_f (ry )
112+ // Python API: experimental.get_odometry() -> (x, y, h)
113+ mp_obj_t experimental_get_odometry ( void ) {
114+ mp_obj_t tuple [ 3 ] = {
115+ mp_obj_new_float_from_f (global_x ),
116+ mp_obj_new_float_from_f (global_y ),
117+ mp_obj_new_float_from_f (global_h )
99118 };
100- return mp_obj_new_tuple (5 , tuple );
119+ return mp_obj_new_tuple (3 , tuple );
120+ }
121+
122+ // Python API: experimental.stop_odometry()
123+ mp_obj_t experimental_stop_odometry (void ) {
124+ odom_running = false;
125+ return mp_const_none ;
101126}
102127
103- // Optimized Pure Pursuit Logic using Dot Products
128+ // ---------------------------------------------------------
129+ // PURE PURSUIT (Unchanged)
130+ // ---------------------------------------------------------
104131mp_obj_t get_pure_pursuit_multipliers (float tx , float ty , float rx_pos , float ry_pos , float rh_ang , float track ) {
105132 float x_dif = tx - rx_pos ;
106133 float y_dif = ty - ry_pos ;
107134
108- float cos_h = fast_sin_internal (rh_ang + 1.5707963f );
135+ float cos_h = fast_cos_internal (rh_ang );
109136 float sin_h = fast_sin_internal (rh_ang );
110137
111138 float relative_y = (y_dif * cos_h ) - (x_dif * sin_h );
0 commit comments