77#include "py/runtime.h"
88#include <math.h>
99
10- // Core Pybricks headers for bare-metal hardware access
1110#include <pybricks/common.h>
1211#include <pbio/servo.h>
1312#include "pybricks/experimental/odometry.h"
13+ #include "pybricks/experimental/platform_math.h"
1414
15- // Hardware acceleration for the Spike Prime
16- #if defined(__ARM_ARCH_7M__ ) || defined(__ARM_ARCH_7EM__ )
17- #define ACCEL_RAM __attribute__((section(".data"), noinline))
18- #else
19- #define ACCEL_RAM
20- #endif
21-
22- ACCEL_RAM static float fast_sin_internal (float theta ) {
23- float x = theta * 0.159154943f ;
24- x = theta - (float )((int )(x + (x > 0.00f ? 0.5f : -0.5f ))) * 6.2831853f ;
25-
26- if (x > 1.5707963f ) {
27- x = 3.1415926f - x ;
28- } else if (x < -1.5707963f ) {
29- x = -3.1415926f - x ;
30- }
31-
32- float x2 = x * x ;
33- return x * (0.99999906f + x2 * (-0.16665554f + x2 * (0.00831190f + x2 * -0.00018488f )));
34- }
35-
36- ACCEL_RAM static float fast_cos_internal (float theta ) {
37- return fast_sin_internal (theta + 1.57079633f );
38- }
15+ // Hardware Pointers
16+ static pbio_servo_t * left_servo_ptr = NULL ;
17+ static pbio_servo_t * right_servo_ptr = NULL ;
3918
40- // ---------------------------------------------------------
41- // BACKGROUND ODOMETRY STATE MACHINE
42- // ---------------------------------------------------------
19+ // Odometry State
4320volatile bool odom_running = false;
4421volatile uint32_t last_odom_time_ms = 0 ;
45-
46- volatile float global_x = 0.0f ;
47- volatile float global_y = 0.0f ;
48- volatile float global_h = 0.0f ;
49-
50- volatile int32_t last_left_angle = 0 ;
51- volatile int32_t last_right_angle = 0 ;
52-
22+ volatile float global_x = 0.0f , global_y = 0.0f , global_h = 0.0f ;
23+ volatile int32_t last_left_angle = 0 , last_right_angle = 0 ;
5324float odom_deg_to_mm = 1.0f ;
5425float odom_inv_track = 1.0f ;
5526
56- pbio_servo_t * left_servo_ptr = NULL ;
57- pbio_servo_t * right_servo_ptr = NULL ;
27+ // Pursuit State
28+ volatile bool pursuit_running = false;
29+ volatile float p_target_x = 0.0f , p_target_y = 0.0f , p_target_speed = 0.0f ;
30+ volatile float p_stop_threshold_sq = 400.0f ;
31+
32+ // --- Background Updates ---
5833
59- // This is the hook that will run continuously in the OS background
6034void pb_background_odometry_update (void ) {
6135 if (!odom_running ) return ;
6236
63- // Throttle to 200Hz (5ms) to prevent starving the motor controllers
6437 uint32_t now = mp_hal_ticks_ms ();
6538 if (now - last_odom_time_ms < 5 ) return ;
6639 last_odom_time_ms = now ;
@@ -75,86 +48,93 @@ void pb_background_odometry_update(void) {
7548 if (delta_l != 0 || delta_r != 0 ) {
7649 float dL = (float )delta_l * odom_deg_to_mm ;
7750 float dR = (float )delta_r * odom_deg_to_mm ;
78-
7951 float dD = (dR + dL ) * 0.5f ;
8052 float dH = (dR - dL ) * odom_inv_track ;
8153 float avg_h = global_h + (dH * 0.5f );
8254
83- global_x += dD * fast_cos_internal (avg_h );
84- global_y += dD * fast_sin_internal (avg_h );
55+ global_x += dD * pb_fast_cos (avg_h );
56+ global_y += dD * pb_fast_sin (avg_h );
8557 global_h += dH ;
8658
8759 last_left_angle = cur_l ;
8860 last_right_angle = cur_r ;
8961 }
9062}
9163
92- // Python API: experimental.start_odometry(left_motor, right_motor, circ, track, x, y, h)
64+ void pb_background_pursuit_update (void ) {
65+ if (!pursuit_running || !left_servo_ptr || !right_servo_ptr ) return ;
66+
67+ float x_dif = p_target_x - global_x ;
68+ float y_dif = p_target_y - global_y ;
69+ float dist_sq = (x_dif * x_dif ) + (y_dif * y_dif );
70+
71+ if (dist_sq < p_stop_threshold_sq ) {
72+ pursuit_running = false;
73+ pbio_servo_stop (left_servo_ptr , PBIO_CONTROL_STOP_BRAKE );
74+ pbio_servo_stop (right_servo_ptr , PBIO_CONTROL_STOP_BRAKE );
75+ return ;
76+ }
77+
78+ float relative_y = (y_dif * pb_fast_cos (global_h )) - (x_dif * pb_fast_sin (global_h ));
79+ float m_left = 1.0f , m_right = 1.0f ;
80+
81+ if (relative_y > 0.001f || relative_y < -0.001f ) {
82+ float radius = - (dist_sq / (2.0f * relative_y ));
83+ float two_r = 2.0f * radius ;
84+ float track = 1.0f / odom_inv_track ;
85+ m_right = two_r / (two_r + track );
86+ m_left = two_r / (two_r - track );
87+ }
88+
89+ pbio_servo_run_velocity (left_servo_ptr , (int32_t )(p_target_speed * m_left ));
90+ pbio_servo_run_velocity (right_servo_ptr , (int32_t )(p_target_speed * m_right ));
91+ }
92+
93+ // --- MicroPython API ---
94+
9395mp_obj_t experimental_start_odometry (size_t n_args , const mp_obj_t * args ) {
9496 left_servo_ptr = ((pb_type_Motor_obj_t * )MP_OBJ_TO_PTR (args [0 ]))-> srv ;
9597 right_servo_ptr = ((pb_type_Motor_obj_t * )MP_OBJ_TO_PTR (args [1 ]))-> srv ;
96-
9798 odom_deg_to_mm = mp_obj_get_float (args [2 ]) / 360.0f ;
9899 odom_inv_track = 1.0f / mp_obj_get_float (args [3 ]);
99-
100100 global_x = mp_obj_get_float (args [4 ]);
101101 global_y = mp_obj_get_float (args [5 ]);
102102 global_h = mp_obj_get_float (args [6 ]);
103103
104104 int32_t unused_rate ;
105105 pbio_servo_get_state_user (left_servo_ptr , (int32_t * )& last_left_angle , & unused_rate );
106106 pbio_servo_get_state_user (right_servo_ptr , (int32_t * )& last_right_angle , & unused_rate );
107-
108107 odom_running = true;
109108 return mp_const_none ;
110109}
111110
112- // Python API: experimental.get_odometry() -> (x, y, h)
113111mp_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 )
118- };
112+ mp_obj_t tuple [3 ] = { mp_obj_new_float_from_f (global_x ), mp_obj_new_float_from_f (global_y ), mp_obj_new_float_from_f (global_h ) };
119113 return mp_obj_new_tuple (3 , tuple );
120114}
121115
122- // Python API: experimental.stop_odometry()
123116mp_obj_t experimental_stop_odometry (void ) {
124117 odom_running = false;
125118 return mp_const_none ;
126119}
127120
128- // ---------------------------------------------------------
129- // PURE PURSUIT (Unchanged)
130- // ---------------------------------------------------------
131- mp_obj_t get_pure_pursuit_multipliers (float tx , float ty , float rx_pos , float ry_pos , float rh_ang , float track ) {
132- float x_dif = tx - rx_pos ;
133- float y_dif = ty - ry_pos ;
134-
135- float cos_h = fast_cos_internal (rh_ang );
136- float sin_h = fast_sin_internal (rh_ang );
137-
138- float relative_y = (y_dif * cos_h ) - (x_dif * sin_h );
139-
140- float m_left = 1.0f ;
141- float m_right = 1.0f ;
142-
143- float abs_rel_y = relative_y < 0.0f ? - relative_y : relative_y ;
121+ mp_obj_t experimental_start_pursuit (size_t n_args , const mp_obj_t * args ) {
122+ p_target_x = mp_obj_get_float (args [0 ]);
123+ p_target_y = mp_obj_get_float (args [1 ]);
124+ p_target_speed = mp_obj_get_float (args [2 ]);
125+ float thresh = mp_obj_get_float (args [3 ]);
126+ p_stop_threshold_sq = thresh * thresh ;
127+ pursuit_running = true;
128+ return mp_const_none ;
129+ }
144130
145- if (abs_rel_y > 0.001f ) {
146- float dist_sq = (x_dif * x_dif ) + (y_dif * y_dif );
147- float radius = - (dist_sq / (2.0f * relative_y ));
148- float two_r = 2.0f * radius ;
149- m_right = two_r / (two_r + track );
150- m_left = two_r / (two_r - track );
131+ mp_obj_t experimental_stop_pursuit (void ) {
132+ pursuit_running = false;
133+ if (left_servo_ptr && right_servo_ptr ) {
134+ pbio_servo_stop (left_servo_ptr , PBIO_CONTROL_STOP_BRAKE );
135+ pbio_servo_stop (right_servo_ptr , PBIO_CONTROL_STOP_BRAKE );
151136 }
152-
153- mp_obj_t tuple [2 ] = {
154- mp_obj_new_float_from_f (m_left ),
155- mp_obj_new_float_from_f (m_right )
156- };
157- return mp_obj_new_tuple (2 , tuple );
137+ return mp_const_none ;
158138}
159139
160140#endif
0 commit comments