Skip to content

Commit e1b2ae6

Browse files
committed
optimize spike
1 parent e54b388 commit e1b2ae6

2 files changed

Lines changed: 120 additions & 21 deletions

File tree

pybricks/experimental/odometry.c

Lines changed: 89 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,91 @@
11
// SPDX-License-Identifier: MIT
22
#include "py/mpconfig.h"
3+
4+
#if PYBRICKS_PY_EXPERIMENTAL
5+
36
#include "py/mphal.h"
47
#include "py/runtime.h"
58
#include <math.h>
6-
#include "pybricks/experimental/platform_math.h"
9+
10+
// Core Pybricks headers for bare-metal hardware access
11+
#include <pybricks/common.h>
12+
#include <pbio/servo.h>
713
#include "pybricks/experimental/odometry.h"
814

9-
mp_obj_t calculate_odometry(int num_iters, float wheel_circ, float axle_track, mp_obj_t right_angle_func, mp_obj_t left_angle_func) {
10-
float deg_to_mm = wheel_circ * 0.0027777778f;
15+
// Hardware acceleration for the Spike Prime (FPU + RAM execution)
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+
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) {
37+
38+
float deg_to_mm = wheel_circ * 0.0027777778f;
1139
float inv_axle_track = 1.0f / axle_track;
12-
float rx = 0.0f, ry = 0.0f, rh = 0.0f;
1340

14-
int32_t last_r = mp_obj_get_int(mp_call_function_0(right_angle_func));
15-
int32_t last_l = mp_obj_get_int(mp_call_function_0(left_angle_func));
41+
float rx = 0.0f;
42+
float ry = 0.0f;
43+
float rh = 0.0f;
44+
45+
// THE MAGIC: Extract the raw Pybricks hardware struct from the Python object.
46+
// This entirely removes the Python interpreter from the loop.
47+
pbio_servo_t *srv_r = ((pb_type_Motor_obj_t *)MP_OBJ_TO_PTR(right_motor_obj))->srv;
48+
pbio_servo_t *srv_l = ((pb_type_Motor_obj_t *)MP_OBJ_TO_PTR(left_motor_obj))->srv;
49+
50+
int32_t last_r, last_l, unused_rate;
51+
// Direct memory read to initialize
52+
pbio_servo_get_state_user(srv_r, &last_r, &unused_rate);
53+
pbio_servo_get_state_user(srv_l, &last_l, &unused_rate);
54+
1655
uint32_t start_time = mp_hal_ticks_ms();
1756

1857
for (int i = 0; i < num_iters; i++) {
19-
int32_t cur_r = mp_obj_get_int(mp_call_function_0(right_angle_func));
20-
int32_t cur_l = mp_obj_get_int(mp_call_function_0(left_angle_func));
58+
int32_t cur_r, cur_l;
59+
60+
// 1. Direct memory read (Instantaneous, 0 Python overhead)
61+
pbio_servo_get_state_user(srv_r, &cur_r, &unused_rate);
62+
pbio_servo_get_state_user(srv_l, &cur_l, &unused_rate);
2163

64+
// 2. FPU Delta Math
2265
float dR = (float)(cur_r - last_r) * deg_to_mm;
2366
float dL = (float)(cur_l - last_l) * deg_to_mm;
2467
float dD = (dR + dL) * 0.5f;
2568
float dH = (dR - dL) * inv_axle_track;
2669

70+
// 3. RK2 Integration
2771
if (dD != 0.0f || dH != 0.0f) {
2872
float avg_h = rh + (dH * 0.5f);
29-
rx += dD * pb_fast_cos(avg_h);
30-
ry += dD * pb_fast_sin(avg_h);
73+
rx += dD * fast_sin_internal(avg_h + 1.5707963f); // cos
74+
ry += dD * fast_sin_internal(avg_h); // sin
3175
rh += dH;
3276
}
77+
3378
last_r = cur_r;
3479
last_l = cur_l;
3580

81+
// 4. Polling (bitwise check is faster than modulo)
3682
if ((i & 0x3FF) == 0) {
3783
mp_handle_pending(true);
3884
}
3985
}
4086

4187
uint32_t dur = mp_hal_ticks_ms() - start_time;
88+
4289
mp_obj_t tuple[5] = {
4390
mp_obj_new_float_from_f((float)dur * 0.001f),
4491
mp_obj_new_int(num_iters),
@@ -48,3 +95,35 @@ mp_obj_t calculate_odometry(int num_iters, float wheel_circ, float axle_track, m
4895
};
4996
return mp_obj_new_tuple(5, tuple);
5097
}
98+
99+
// Optimized Pure Pursuit Logic using Dot Products
100+
mp_obj_t get_pure_pursuit_multipliers(float tx, float ty, float rx_pos, float ry_pos, float rh_ang, float track) {
101+
float x_dif = tx - rx_pos;
102+
float y_dif = ty - ry_pos;
103+
104+
float cos_h = fast_sin_internal(rh_ang + 1.5707963f);
105+
float sin_h = fast_sin_internal(rh_ang);
106+
107+
float relative_y = (y_dif * cos_h) - (x_dif * sin_h);
108+
109+
float m_left = 1.0f;
110+
float m_right = 1.0f;
111+
112+
float abs_rel_y = relative_y < 0.0f ? -relative_y : relative_y;
113+
114+
if (abs_rel_y > 0.001f) {
115+
float dist_sq = (x_dif * x_dif) + (y_dif * y_dif);
116+
float radius = -(dist_sq / (2.0f * relative_y));
117+
float two_r = 2.0f * radius;
118+
m_right = two_r / (two_r + track);
119+
m_left = two_r / (two_r - track);
120+
}
121+
122+
mp_obj_t tuple[2] = {
123+
mp_obj_new_float_from_f(m_left),
124+
mp_obj_new_float_from_f(m_right)
125+
};
126+
return mp_obj_new_tuple(2, tuple);
127+
}
128+
129+
#endif
Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,46 @@
11
#include "py/mpconfig.h"
22
#include "py/obj.h"
33
#include "py/runtime.h"
4-
#include "pybricks/experimental/odometry.h"
54

6-
static mp_obj_t experimental_odometry_benchmark(size_t n_args, const mp_obj_t *args) {
7-
int num_iters = mp_obj_get_int(args[0]);
8-
float wheel_circ = mp_obj_get_float(args[1]);
9-
float axle_track = mp_obj_get_float(args[2]);
10-
mp_obj_t right_func = args[3];
11-
mp_obj_t left_func = args[4];
5+
#if PYBRICKS_PY_EXPERIMENTAL
6+
7+
// Forward declare the C functions
8+
extern mp_obj_t calculate_odometry(int num_iters, float wheel_circ, float axle_track, mp_obj_t right_motor, mp_obj_t left_motor);
9+
extern mp_obj_t get_pure_pursuit_multipliers(float tx, float ty, float rx_pos, float ry_pos, float rh_ang, float track);
1210

13-
return calculate_odometry(num_iters, wheel_circ, axle_track, right_func, left_func);
11+
static mp_obj_t experimental_odometry_benchmark(size_t n_args, const mp_obj_t *args) {
12+
return calculate_odometry(
13+
mp_obj_get_int(args[0]),
14+
mp_obj_get_float(args[1]),
15+
mp_obj_get_float(args[2]),
16+
args[3], // Raw Python Motor Object (Right)
17+
args[4] // Raw Python Motor Object (Left)
18+
);
1419
}
1520
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(experimental_odometry_benchmark_obj, 5, 5, experimental_odometry_benchmark);
1621

22+
static mp_obj_t experimental_pure_pursuit_logic(size_t n_args, const mp_obj_t *args) {
23+
return get_pure_pursuit_multipliers(
24+
mp_obj_get_float(args[0]),
25+
mp_obj_get_float(args[1]),
26+
mp_obj_get_float(args[2]),
27+
mp_obj_get_float(args[3]),
28+
mp_obj_get_float(args[4]),
29+
mp_obj_get_float(args[5])
30+
);
31+
}
32+
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(experimental_pure_pursuit_logic_obj, 6, 6, experimental_pure_pursuit_logic);
33+
1734
const mp_rom_map_elem_t pb_module_experimental_globals_table[] = {
18-
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_experimental) },
19-
{ MP_ROM_QSTR(MP_QSTR_odometry_benchmark), MP_ROM_PTR(&experimental_odometry_benchmark_obj) },
35+
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_experimental) },
36+
{ MP_ROM_QSTR(MP_QSTR_odometry_benchmark), MP_ROM_PTR(&experimental_odometry_benchmark_obj) },
37+
{ MP_ROM_QSTR(MP_QSTR_pure_pursuit_logic), MP_ROM_PTR(&experimental_pure_pursuit_logic_obj) },
2038
};
2139
MP_DEFINE_CONST_DICT(pb_module_experimental_globals, pb_module_experimental_globals_table);
2240

2341
const mp_obj_module_t pb_module_experimental = {
2442
.base = { &mp_type_module },
2543
.globals = (mp_obj_dict_t *)&pb_module_experimental_globals,
26-
};
44+
};
45+
46+
#endif // PYBRICKS_PY_EXPERIMENTAL

0 commit comments

Comments
 (0)