Compare commits

...

9 commits
master ... pid2

Author SHA1 Message Date
Zoltán Vörös
0f10e24147 update docs 2024-03-06 18:52:03 +01:00
Zoltán Vörös
3648d64fbe add system time to PID loop function 2024-03-06 12:46:20 +01:00
Zoltán Vörös
55a4a256d1 update documentation, clean up code 2024-03-06 11:59:03 +01:00
Zoltán Vörös
81f564f689 replace getters/setters by attributes 2024-03-05 18:02:03 +01:00
Zoltán Vörös
70e364ae4b factor out re-used code into function 2024-02-20 22:48:21 +01:00
Zoltán Vörös
c3b541f043 remove redundant max value 2024-02-20 22:38:49 +01:00
Zoltán Vörös
a765c35dc1 first implemenation of all functions; add rudimentary documentation 2024-02-20 22:29:43 +01:00
Zoltán Vörös
a9fec6461f add some helper methods 2024-02-10 18:53:46 +01:00
Zoltán Vörös
9e2b1c22dd implement scaffold of PID module 2024-01-28 22:06:23 +01:00
6 changed files with 1671 additions and 2 deletions

View file

@ -31,6 +31,7 @@ SRC_USERMOD += $(USERMODULES_DIR)/numpy/transform.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/vector.c
SRC_USERMOD += $(USERMODULES_DIR)/numpy/numpy.c
SRC_USERMOD += $(USERMODULES_DIR)/pid/pid.c
SRC_USERMOD += $(USERMODULES_DIR)/scipy/scipy.c
SRC_USERMOD += $(USERMODULES_DIR)/user/user.c
SRC_USERMOD += $(USERMODULES_DIR)/utils/utils.c

497
code/pid/pid.c Normal file
View file

@ -0,0 +1,497 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2024 Zoltán Vörös
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "py/mphal.h"
#include "py/runtime.h"
#include "py/misc.h"
#include "py/obj.h"
#include "py/objstr.h"
#include "py/objtuple.h"
#include "pid.h"
#if ULAB_HAS_PID_MODULE
// methods of the PID buffer object
static const mp_rom_map_elem_t ulab_pid_buffer_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_evaluate), MP_ROM_PTR(&pid_buffer_evaluate_obj) },
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pid_buffer_init_obj) },
};
static MP_DEFINE_CONST_DICT(ulab_pid_buffer_locals_dict, ulab_pid_buffer_locals_dict_table);
// attributes of the PID buffer object
static void pid_buffer_attributes(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
if(dest[0] == MP_OBJ_NULL) {
switch(attr) {
case MP_QSTR_order:
dest[0] = pid_buffer_getter(self_in, PID_BUFFER_ORDER);
break;
case MP_QSTR_x0:
dest[0] = pid_buffer_getter(self_in, PID_BUFFER_X0);
break;
case MP_QSTR_coeffs:
dest[0] = pid_buffer_getter(self_in, PID_BUFFER_COEFFS);
break;
case MP_QSTR_offset:
dest[0] = pid_buffer_getter(self_in, PID_BUFFER_OFFSET);
break;
case MP_QSTR_bitdepth:
dest[0] = pid_buffer_getter(self_in, PID_BUFFER_BITDEPTH);
break;
case MP_QSTR_mask:
dest[0] = pid_buffer_getter(self_in, PID_BUFFER_MASK);
break;
case MP_QSTR_bytes:
dest[0] = pid_buffer_getter(self_in, PID_BUFFER_BYTES);
break;
default:
// forward to locals dict
dest[1] = MP_OBJ_SENTINEL;
break;
}
} else {
if(dest[1]) {
switch(attr) {
case MP_QSTR_x0:
pid_buffer_setter(self_in, dest[1], PID_BUFFER_X0);
break;
case MP_QSTR_coeffs:
pid_buffer_setter(self_in, dest[1], PID_BUFFER_COEFFS);
break;
case MP_QSTR_offset:
pid_buffer_setter(self_in, dest[1], PID_BUFFER_OFFSET);
break;
case MP_QSTR_bitdepth:
pid_buffer_setter(self_in, dest[1], PID_BUFFER_BITDEPTH);
break;
case MP_QSTR_bytes:
pid_buffer_setter(self_in, dest[1], PID_BUFFER_BYTES);
break;
default:
return;
break;
}
dest[0] = MP_OBJ_NULL;
}
}
}
MP_DEFINE_CONST_OBJ_TYPE(
ulab_pid_buffer_type,
MP_QSTR_buffer,
MP_TYPE_FLAG_NONE,
print, pid_buffer_print,
make_new, pid_buffer_make_new,
locals_dict, &ulab_pid_buffer_locals_dict,
attr, pid_buffer_attributes
);
static pid_buffer_obj_t *pid_buffer_create(void) {
pid_buffer_obj_t *buffer = m_new_obj(pid_buffer_obj_t);
buffer->base.type = &ulab_pid_buffer_type;
return buffer;
}
mp_obj_t pid_buffer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
(void) type;
mp_arg_check_num(n_args, n_kw, 0, 2, true);
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
return MP_OBJ_FROM_PTR(pid_buffer_create());
}
void pid_buffer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind;
pid_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(MP_PYTHON_PRINTER, "PID buffer object at 0x%p", self);
}
mp_obj_t pid_buffer_getter(mp_obj_t self_in, uint8_t attribute) {
pid_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in);
if(attribute == PID_BUFFER_ORDER) {
return MP_OBJ_NEW_SMALL_INT(self->series.order);
} else if(attribute == PID_BUFFER_X0) {
return mp_obj_new_float(self->series.x0);
} else if(attribute == PID_BUFFER_COEFFS) {
mp_float_t *coeffs = (mp_float_t *)self->series.coeffs;
mp_obj_t *items = m_new(mp_obj_t, self->series.order);
for(uint8_t i = 0; i < self->series.order; i++) {
items[i] = mp_obj_new_float(*coeffs++);
}
mp_obj_t tuple = mp_obj_new_tuple(self->series.order, items);
return tuple;
} else if(attribute == PID_BUFFER_OFFSET) {
return MP_OBJ_NEW_SMALL_INT(self->converter.offset);
} else if(attribute == PID_BUFFER_BITDEPTH) {
return MP_OBJ_NEW_SMALL_INT(self->converter.bitdepth);
} else if(attribute == PID_BUFFER_MASK) {
return MP_OBJ_NEW_SMALL_INT(self->converter.mask);
} else if(attribute == PID_BUFFER_BYTES) {
return mp_obj_new_bytearray_by_ref(self->converter.len, self->converter.bytes);
}
return mp_const_none;
}
mp_obj_t pid_buffer_setter(mp_obj_t self_in, mp_obj_t value, uint8_t attribute) {
pid_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in);
if(attribute == PID_BUFFER_X0) {
self->series.x0 = mp_obj_get_float(value);
} else if(attribute == PID_BUFFER_COEFFS) {
mp_float_t *coeffs = (mp_float_t *)self->series.coeffs;
m_del(mp_float_t, coeffs, self->series.order);
uint8_t order = (uint8_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(value));
coeffs = m_new0(mp_float_t, order);
self->series.order = order;
self->series.coeffs = coeffs;
mp_obj_iter_buf_t buf;
mp_obj_t item, iterable = mp_getiter(value, &buf);
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
*coeffs++ = (mp_float_t)mp_obj_get_float(item);
}
} else if(attribute == PID_BUFFER_OFFSET) {
self->converter.offset = (uint8_t)MP_OBJ_SMALL_INT_VALUE(value);
} else if(attribute == PID_BUFFER_BITDEPTH) {
uint8_t bitdepth = (uint8_t)MP_OBJ_SMALL_INT_VALUE(value);
self->converter.bitdepth = bitdepth;
// TODO: we might have to consider the offset here!!!
self->converter.mask = (1 << bitdepth) - 1;
self->converter.nbytes = (bitdepth + 7) / 8;
} else if(attribute == PID_BUFFER_BYTES) {
mp_buffer_info_t bufinfo;
if(mp_get_buffer(value, &bufinfo, MP_BUFFER_READ)) {
if(bufinfo.len < (self->converter.offset + self->converter.bitdepth) / 8) {
mp_raise_ValueError(MP_ERROR_TEXT("buffer is shorter than offset plus bitdepth"));
}
}
self->converter.len = bufinfo.len;
self->converter.bytes = bufinfo.buf;
}
return mp_const_none;
}
static mp_float_t pid_pid_convert(pid_series_t series, mp_float_t x) {
// taking the coefficients of the Taylor expansion, returns the
// approximate value of a function at the value x
mp_float_t dx = x - series.x0;
mp_float_t *coeffs = (mp_float_t *)series.coeffs;
mp_float_t y = *coeffs++;
for(uint8_t i = 0; i < series.order - 1; i++) {
y *= dx;
y += *coeffs++;
}
return y;
}
static mp_obj_t pid_buffer_evaluate(mp_obj_t self_in, mp_obj_t x) {
// convenience function for manually checking the conversion
pid_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_float(pid_pid_convert(self->series, mp_obj_get_float(x)));
}
MP_DEFINE_CONST_FUN_OBJ_2(pid_buffer_evaluate_obj, pid_buffer_evaluate);
static mp_obj_t pid_buffer_init(mp_obj_t self_in) {
// initialise buffer with some default values
pid_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in);
self->series.order = 2;
self->series.x0 = MICROPY_FLOAT_CONST(0.0);
self->series.coeffs = m_new0(mp_float_t, 2);
self->series.coeffs[0] = MICROPY_FLOAT_CONST(1.0);
self->converter.len = 2;
self->converter.bitdepth = 16;
self->converter.mask = 0xffff;
self->converter.offset = 0;
self->converter.bytes = (uint8_t *)&self->converter.mask;
self->converter.nbytes = 2;
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(pid_buffer_init_obj, pid_buffer_init);
// methods of the PID object
static const mp_rom_map_elem_t ulab_pid_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&pid_pid_reset_obj) },
{ MP_ROM_QSTR(MP_QSTR_float_step), MP_ROM_PTR(&pid_pid_float_step_obj) },
{ MP_ROM_QSTR(MP_QSTR_step), MP_ROM_PTR(&pid_pid_step_obj) },
};
static MP_DEFINE_CONST_DICT(ulab_pid_locals_dict, ulab_pid_locals_dict_table);
// attributes of the PID object; the input/output buffers are defer to till later
static void pid_pid_attributes(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
if (dest[0] == MP_OBJ_NULL) {
switch(attr) {
case MP_QSTR_P:
dest[0] = pid_pid_parameters(self_in, MP_OBJ_NULL, PID_PARAMETER_P);
break;
case MP_QSTR_I:
dest[0] = pid_pid_parameters(self_in, MP_OBJ_NULL, PID_PARAMETER_I);
break;
case MP_QSTR_D:
dest[0] = pid_pid_parameters(self_in, MP_OBJ_NULL, PID_PARAMETER_D);
break;
case MP_QSTR_setpoint:
dest[0] = pid_pid_parameters(self_in, MP_OBJ_NULL, PID_SETPOINT);
break;
case MP_QSTR_steps:
dest[0] = pid_pid_parameters(self_in, MP_OBJ_NULL, PID_STEPS);
break;
case MP_QSTR_value:
dest[0] = pid_pid_parameters(self_in, MP_OBJ_NULL, PID_VALUE);
break;
case MP_QSTR_out:
dest[0] = pid_pid_parameters(self_in, MP_OBJ_NULL, PID_OUT);
break;
case MP_QSTR_input:
dest[0] = pid_return_buffer(self_in, MP_OBJ_NULL, PID_BUFFER_INPUT);
break;
case MP_QSTR_output:
dest[0] = pid_return_buffer(self_in, MP_OBJ_NULL, PID_BUFFER_OUTPUT);
break;
default:
// forward to locals dict
dest[1] = MP_OBJ_SENTINEL;
break;
}
} else {
if(dest[1]) {
switch(attr) {
case MP_QSTR_P:
pid_pid_parameters(self_in, dest[1], PID_PARAMETER_P);
break;
case MP_QSTR_I:
pid_pid_parameters(self_in, dest[1], PID_PARAMETER_I);
break;
case MP_QSTR_D:
pid_pid_parameters(self_in, dest[1], PID_PARAMETER_D);
break;
case MP_QSTR_setpoint:
pid_pid_parameters(self_in, dest[1], PID_SETPOINT);
break;
default:
return;
break;
}
dest[0] = MP_OBJ_NULL;
}
}
}
MP_DEFINE_CONST_OBJ_TYPE(
ulab_pid_type,
MP_QSTR_PID,
MP_TYPE_FLAG_NONE,
print, pid_pid_print,
make_new, pid_pid_make_new,
locals_dict, &ulab_pid_locals_dict,
attr, pid_pid_attributes
);
void pid_pid_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind;
pid_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(MP_PYTHON_PRINTER, "PID object at 0x%p", self);
}
mp_obj_t pid_pid_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
(void) type;
mp_arg_check_num(n_args, n_kw, 0, 2, true);
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
pid_obj_t *self = m_new_obj(pid_obj_t);
self->base.type = &ulab_pid_type;
self->setpoint = MICROPY_FLOAT_CONST(0.0);
self->input = pid_buffer_create();
self->output = pid_buffer_create();
self->P = MICROPY_FLOAT_CONST(0.0);
self->I = MICROPY_FLOAT_CONST(0.0);
self->D = MICROPY_FLOAT_CONST(0.0);
self->integral = MICROPY_FLOAT_CONST(0.0);
self->value = MICROPY_FLOAT_CONST(0.0);
self->error = MICROPY_FLOAT_CONST(0.0);
self->time_us = 0L;
self->out = MICROPY_FLOAT_CONST(0.0);
self->steps = 0;
return self;
}
mp_obj_t pid_return_buffer(mp_obj_t self_in, mp_obj_t value, uint8_t designator) {
pid_obj_t *self = MP_OBJ_TO_PTR(self_in);
if(designator == PID_BUFFER_INPUT) {
return self->input;
} else {
return self->output;
}
}
mp_obj_t pid_pid_parameters(mp_obj_t self_in, mp_obj_t value, uint8_t attribute) {
pid_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_float_t *parameter;
if(attribute == PID_STEPS) {
if(value == MP_OBJ_NULL) {
return MP_OBJ_NEW_SMALL_INT(self->steps);
} // internal variable, no setter for this
} {
if(attribute == PID_PARAMETER_P) {
parameter = &(self->P);
} else if(attribute == PID_PARAMETER_I) {
parameter = &(self->I);
} else if(attribute == PID_PARAMETER_D) {
parameter = &(self->D);
} else if(attribute == PID_SETPOINT) {
parameter = &(self->setpoint);
} else if(attribute == PID_OUT) {
parameter = &(self->out);
}
else {
parameter = &(self->value);
}
if(value == MP_OBJ_NULL) {
return mp_obj_new_float(*parameter);
} else {
*parameter = mp_obj_get_float(value);
}
}
return mp_const_none;
}
mp_obj_t pid_pid_reset(mp_obj_t _self) {
// resets only the PID values but not the parameters
pid_obj_t *self = MP_OBJ_TO_PTR(_self);
self->integral = MICROPY_FLOAT_CONST(0.0);
self->value = MICROPY_FLOAT_CONST(0.0);
self->steps = 0;
self->out = MICROPY_FLOAT_CONST(0.0);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(pid_pid_reset_obj, pid_pid_reset);
mp_float_t pid_float_time_ms(pid_obj_t *self) {
// calculates the time difference with respect to the last call
mp_uint_t end = mp_hal_ticks_us();
mp_float_t dt_ms;
if(end > self->time_us) {
dt_ms = 0.001 * (end - self->time_us);
} else { // we have a time warp here, so swap
dt_ms = 0.001 * (self->time_us - end);
}
self->time_us = end;
return dt_ms;
}
void pid_pid_loop(pid_obj_t *self, mp_float_t value, mp_float_t dt) {
// This is the standard PID loop, stripped of everything
mp_float_t error = value - self->setpoint;
mp_float_t diff = (error - self->error) / dt;
self->integral += error * dt;
self->out = self->P * error + self->I * self->integral + self->D * diff;
self->error = error;
self->value = value;
self->steps++;
}
mp_obj_t pid_pid_step(mp_obj_t self_in) {
// The complete PID loop, converting the value from the input buffer,
// using the value in the PID loop, and then converting the results to the output buffer
pid_obj_t *self = MP_OBJ_TO_PTR(self_in);
// // convert value in input buffer
// // assume for now that all bytes in the buffer are significant bytes (i.e., there are no auxiliary bytes)
uint32_t input = 0; // 32 bits should suffice for most ADCs
uint8_t *input_bytes = (uint8_t *)&input;
uint8_t *buffer_bytes = (uint8_t *)self->input->converter.bytes;
input_bytes += 4;
buffer_bytes += self->input->converter.len;
for(uint8_t i = 0; i < self->input->converter.len; i++) {
*(--input_bytes) = *(--buffer_bytes);
}
mp_float_t x = (mp_float_t)(input & self->input->converter.mask);
mp_float_t value = pid_pid_convert(self->input->series, x);
mp_float_t dt_ms = pid_float_time_ms(self);
pid_pid_loop(self, value, dt_ms);
// convert value in output buffer
value = pid_pid_convert(self->output->series, self->out);
// ensure that output is within limits
if(self->output->converter.bitdepth != 0) {
value = (value < 0 ? 0 : value);
value = (value > (mp_float_t)self->output->converter.mask ? (mp_float_t)self->output->converter.mask : value);
}
uint32_t output = ((uint32_t)value) & self->output->converter.mask;
uint8_t *output_bytes = (uint8_t *)&output;
buffer_bytes = (uint8_t *)self->output->converter.bytes;
output_bytes += 4;
buffer_bytes += self->output->converter.len;
for(uint8_t i = 0; i < self->output->converter.len; i++) {
*(--buffer_bytes) = *(--output_bytes);
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(pid_pid_step_obj, pid_pid_step);
mp_obj_t pid_pid_float_step(size_t n_args, const mp_obj_t *args) {
// The simplified PID loop, working with floating points numbers directly
pid_obj_t *self = MP_OBJ_TO_PTR(args[0]);
mp_float_t dt = 1.0;
if(n_args == 3) {
dt = mp_obj_get_float(args[2]);
}
mp_float_t value = mp_obj_get_float(args[1]);
pid_pid_loop(self, value, dt);
return mp_obj_new_float(self->out);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pid_pid_float_step_obj, 2, 3, pid_pid_float_step);
static const mp_rom_map_elem_t ulab_pid_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_PID) },
{ MP_ROM_QSTR(MP_QSTR_PID), MP_ROM_PTR(&ulab_pid_type) },
{ MP_ROM_QSTR(MP_QSTR_buffer), MP_ROM_PTR(&ulab_pid_buffer_type) },
};
static MP_DEFINE_CONST_DICT(mp_module_ulab_pid_globals, ulab_pid_globals_table);
const mp_obj_module_t ulab_pid_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_ulab_pid_globals,
};
#endif /* ULAB_HAS_PID_MODULE */

104
code/pid/pid.h Normal file
View file

@ -0,0 +1,104 @@
/*
* This file is part of the micropython-ulab project,
*
* https://github.com/v923z/micropython-ulab
*
* The MIT License (MIT)
*
* Copyright (c) 2024 Zoltán Vörös
*/
#ifndef _PID_
#define _PID_
#include "../ulab.h"
extern const mp_obj_module_t ulab_pid_module;
extern const mp_obj_type_t ulab_pid_type;
enum PID_PARAMETER {
PID_PARAMETER_P,
PID_PARAMETER_I,
PID_PARAMETER_D,
PID_SETPOINT,
PID_STEPS,
PID_VALUE,
PID_OUT,
};
enum PID_BUFFER {
PID_BUFFER_INPUT,
PID_BUFFER_OUTPUT,
PID_BUFFER_BITDEPTH,
PID_BUFFER_BYTES,
PID_BUFFER_COEFFS,
PID_BUFFER_MASK,
PID_BUFFER_OFFSET,
PID_BUFFER_ORDER,
PID_BUFFER_X0,
};
// structure holding the Taylor series representation of
// the conversion of physical values to unitless numbers
typedef struct _pid_series_t {
uint8_t order; // order of the Taylor expansion
mp_float_t x0; // the function is expanded around this point
mp_float_t *coeffs; // pointer to coefficients of the Taylor series
} pid_series_t;
// structure holding the description and byte data of the input/output buffers
typedef struct _pid_converter_t {
uint8_t len; // length of the input/output buffer, bytes
uint8_t bitdepth; // resolution of ADC/DAC, bits
uint32_t mask; // mask to get rid of non-numerical data (calculated)
uint8_t offset; // offset of beginning of data relative to beginning of buffer, bytes
uint8_t *bytes; // pointer to bytes of the buffer
uint8_t nbytes; // number of bytes in value; calculated from bitdepth
} pid_converter_t;
typedef struct _pid_buffer_t {
pid_converter_t converter;
pid_series_t series;
} pid_buffer_t;
typedef struct _pid_buffer_obj_t {
mp_obj_base_t base;
pid_converter_t converter;
pid_series_t series;
} pid_buffer_obj_t;
typedef struct _pid_obj_t {
mp_obj_base_t base;
mp_float_t setpoint; // set point of the controller loop
pid_buffer_obj_t *input; // the input buffer
pid_buffer_obj_t *output; // the output buffer
mp_float_t P; // coefficient of the proportional term
mp_float_t I; // coefficient of the integral term
mp_float_t D; // coefficient of the differential term
mp_float_t integral; // the last value of the integral term
mp_float_t out; // the output value
mp_float_t value; // the last converted value supplied to the loop
mp_float_t error; // the last calculated error; the difference between value and set_point
mp_uint_t time_us; // absolute system time, us
uint64_t steps; // the step method has been called this many times
} pid_obj_t;
mp_obj_t pid_buffer_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *);
void pid_buffer_print(const mp_print_t *, mp_obj_t , mp_print_kind_t );
mp_obj_t pid_buffer_getter(mp_obj_t , uint8_t );
mp_obj_t pid_buffer_setter(mp_obj_t , mp_obj_t , uint8_t );
MP_DECLARE_CONST_FUN_OBJ_2(pid_buffer_evaluate_obj);
MP_DECLARE_CONST_FUN_OBJ_1(pid_buffer_init_obj);
mp_obj_t pid_pid_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *);
void pid_pid_print(const mp_print_t *, mp_obj_t , mp_print_kind_t );
mp_obj_t pid_pid_parameters(mp_obj_t , mp_obj_t , uint8_t );
mp_obj_t pid_return_buffer(mp_obj_t , mp_obj_t , uint8_t );
MP_DECLARE_CONST_FUN_OBJ_1(pid_pid_reset_obj);
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(pid_pid_float_step_obj);
MP_DECLARE_CONST_FUN_OBJ_1(pid_pid_step_obj);
#endif

View file

@ -26,6 +26,7 @@
#include "numpy/ndarray/ndarray_iter.h"
#include "numpy/numpy.h"
#include "pid/pid.h"
#include "scipy/scipy.h"
// TODO: we should get rid of this; array.sort depends on it
#include "numpy/numerical.h"
@ -33,7 +34,7 @@
#include "user/user.h"
#include "utils/utils.h"
#define ULAB_VERSION 6.5.0
#define ULAB_VERSION 6.6.0
#define xstr(s) str(s)
#define str(s) #s
@ -205,7 +206,10 @@ STATIC const mp_rom_map_elem_t ulab_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_dtype), MP_ROM_PTR(&ndarray_dtype_obj) },
#endif /* NDARRAY_HAS_DTYPE */
#endif /* ULAB_HAS_DTYPE_OBJECT */
{ MP_ROM_QSTR(MP_QSTR_numpy), MP_ROM_PTR(&ulab_numpy_module) },
{ MP_ROM_QSTR(MP_QSTR_numpy), MP_ROM_PTR(&ulab_numpy_module) },
#if ULAB_HAS_PID_MODULE
{ MP_ROM_QSTR(MP_QSTR_PID), MP_ROM_PTR(&ulab_pid_module) },
#endif
#if ULAB_HAS_SCIPY
{ MP_ROM_QSTR(MP_QSTR_scipy), MP_ROM_PTR(&ulab_scipy_module) },
#endif

View file

@ -714,6 +714,10 @@
#define ULAB_NUMPY_RANDOM_HAS_UNIFORM (1)
#endif
// PID module
#ifndef ULAB_HAS_PID_MODULE
#define ULAB_HAS_PID_MODULE (1)
#endif
// scipy modules
#ifndef ULAB_SCIPY_HAS_LINALG_MODULE

1059
docs/ulab-pid.ipynb Normal file

File diff suppressed because it is too large Load diff