Вопрос или проблема
Я работаю над проектом по вычислению расстояний между маяками и тегом с использованием триангуляции и фильтрации Калмана, среди прочих методов. Я поделился основной частью моего кода ниже. Я использую модули UWB DW1000 и программирую в среде разработки Keil 5.36.
Это мой первый опыт работы с чем-то подобным, и я немного теряюсь. Я пытаюсь автоматизировать расчеты расстояний, чтобы вычислить координаты тега, а также автоматизировать расчеты углов. В конце я хочу отобразить расстояния от Маяка 1, Маяка 2 и Маяка 3 до тега.
Может ли кто-то указать, что может быть не так с кодом? Нужно ли очищать какую-либо память или стоит переосмыслить, как устроены функции? Я был бы очень признателен за любую помощь; я находюсь в тупике!
Извините за комментарии на другом языке, у меня нет сил переводить их. 🙁
#include "stm32f10x.h" // Стандартная библиотека для работы с STM32
#include
#include
#include "deca_device_api.h" // API для взаимодействия с устройством DW1000
#include "deca_regs.h" // Регистры DW1000
#include "deca_sleep.h" // Функции для организации задержек
#include "lcd.h" // Работа с LCD дисплеем
#include "port.h" // Порты и интерфейсы
#include "lcd_oled.h" // Драйвер OLED дисплея
#include "trilateration.h" // Модуль для работы с триангуляцией
#include
#include "kalman.h"
#include "AT24C02.h" // Работа с EEPROM
#include "stm32_eval.h" // Библиотека для отладки STM32
#include "lib.h"
extern char dist_str[16]; // Строка для отображения дистанции
extern uint8_t TAG_ID; // Идентификатор метки
extern uint8_t MASTER_TAG; // Идентификатор главной метки
extern uint8_t SLAVE_TAG_START_INDEX; // Начальный индекс меток-слейвов
extern uint8_t ANCHOR_IND;
extern uint8 Semaphore[MAX_SLAVE_TAG];
/* Массив с координатами якорей */
vec3d AnchorList[ANCHOR_MAX_NUM];
/* Лучшее решение для позиции метки */
vec3d tag_best_solution;
/* Массив расстояний до якорей */
int Anthordistance[ANCHOR_MAX_NUM];
/* Счётчик количества измерений для каждого якоря */
int Anthordistance_count[ANCHOR_MAX_NUM];
/* Константа для обновления расстояний до якорей */
int ANCHOR_REFRESH_COUNT_set = 5;
#define ANCHOR_REFRESH_COUNT ANCHOR_REFRESH_COUNT_set
#ifdef __GNUC__
/* Если используется компилятор GCC/RAISONANCE, маленькая версия printf вызывает __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE * f)
#endif /* __GNUC__ */
/* Функция для вывода содержимого регистров */
void dwt_dumpregisters(char * str, size_t strSize) {
uint32 reg = 0; // Переменная для хранения значения регистра
uint8 buff[5]; // Буфер для чтения данных из устройства
int i; // Индекс для циклов
int cnt;
#if(0)
// Чтение и вывод регистров от 0x00 до 0x3F
for (i = 0; i < 0x3F; i++) { // Цикл для чтения всех регистров от 0 до 0x3F
dwt_readfromdevice(i, 0, 5, buff);
// Форматируем строку с результатом и добавляем в строку str
str += cnt = sprintf(str, "reg[%02X]=%02X%02X%02X%02X%02X", i, buff[4], buff[3], buff[2], buff[1], buff[0]); // Форматирование строки с данными
str += cnt = sprintf(str, "\n");
}
// Чтение регистров для регистров 0x20
for (i = 0; i <= 32; i += 4) {
reg = dwt_read32bitoffsetreg(0x20, i);
str += cnt = sprintf(str, "reg[%02X:%02X]=%08X", 0x20, i, reg);
str += cnt = sprintf(str, "\n");
}
// Чтение регистров для регистров 0x21
for (i = 0; i <= 44; i += 4) {
reg = dwt_read32bitoffsetreg(0x21, i);
str += cnt = sprintf(str, "reg[%02X:%02X]=%08X", 0x21, i, reg);
str += cnt = sprintf(str, "\n");
}
// Чтение регистров для регистров 0x23
for (i = 0; i <= 0x20; i += 4) {
reg = dwt_read32bitoffsetreg(0x23, i);
str += cnt = sprintf(str, "reg[%02X:%02X]=%08X", 0x23, i, reg);
str += cnt = sprintf(str, "\n");
}
#else
// Чтение регистров для регистров 0x24
for (i = 0; i <= 12; i += 4) {
reg = dwt_read32bitoffsetreg(0x24, i);
str += cnt = sprintf(str, "reg[%02X:%02X]=%08X", 0x24, i, reg);
str += cnt = sprintf(str, "\n");
}
// Чтение регистров для регистров 0x27
for (i = 0; i <= 44; i += 4) {
reg = dwt_read32bitoffsetreg(0x27, i);
str += cnt = sprintf(str, "reg[%02X:%02X]=%08X", 0x27, i, reg);
str += cnt = sprintf(str, "\n");
}
// Чтение регистров для регистров 0x28
for (i = 0; i <= 64; i += 4) {
reg = dwt_read32bitoffsetreg(0x28, i);
str += cnt = sprintf(str, "reg[%02X:%02X]=%08X", 0x28, i, reg);
str += cnt = sprintf(str, "\n");
}
// Чтение регистров для регистров 0x2A
for (i = 0; i < 20; i += 4) {
reg = dwt_read32bitoffsetreg(0x2A, i);
str += cnt = sprintf(str, "reg[%02X:%02X]=%08X", 0x2A, i, reg);
str += cnt = sprintf(str, "\n");
}
// Чтение регистров для регистров 0x2B
for (i = 0; i < 24; i += 4) {
reg = dwt_read32bitoffsetreg(0x2B, i);
str += cnt = sprintf(str, "reg[%02X:%02X]=%08X", 0x2B, i, reg);
str += cnt = sprintf(str, "\n");
}
// Чтение регистров для регистров 0x2f
for (i = 0; i < 40; i += 4) {
reg = dwt_read32bitoffsetreg(0x2f, i);
str += cnt = sprintf(str, "reg[%02X:%02X]=%08X", 0x2f, i, reg);
str += cnt = sprintf(str, "\n");
}
// Чтение регистров для регистров 0x31
for (i = 0; i < 84; i += 4) {
reg = dwt_read32bitoffsetreg(0x31, i);
str += cnt = sprintf(str, "reg[%02X:%02X]=%08X", 0x31, i, reg);
str += cnt = sprintf(str, "\n");
}
// Чтение регистров для регистров 0x36 = PMSC_ID
for (i = 0; i <= 48; i += 4) {
reg = dwt_read32bitoffsetreg(0x36, i);
str += cnt = sprintf(str, "reg[%02X:%02X]=%08X", 0x36, i, reg);
str += cnt = sprintf(str, "\n");
}
#endif
}
void Anchor_Array_Init(void) {
int anchor_index = 0;
for (anchor_index = 0; anchor_index < ANCHOR_MAX_NUM; anchor_index++) {
Anthordistance[anchor_index] = 0; // Обнуляем расстояние
Anthordistance_count[anchor_index] = 0; // Обнуляем счётчик измерений
}
}
void Semaphore_Init(void) {
int tag_index = 0;
// Инициализируем семафоры для всех меток
for (tag_index = 0; tag_index < MAX_SLAVE_TAG; tag_index++) {
Semaphore[tag_index] = 0; // Все семафоры устанавливаем в 0 (неактивные)
}
}
int Sum_Tag_Semaphore_request(void) {
int tag_index = 0;
int sum_request = 0;
for (tag_index = SLAVE_TAG_START_INDEX; tag_index < MAX_SLAVE_TAG; tag_index++) {
sum_request += Semaphore[tag_index]; // Суммируем значение семафора
}
return sum_request; // Возвращаем общее количество запросов
}
void Tag_Measure_Dis(void) {
uint8 dest_anthor = 0, frame_len = 0;
float final_distance = 0;
frame_seq_nb = 0;
for (dest_anthor = 0; dest_anthor < ANCHOR_MAX_NUM; dest_anthor++) {
dwt_setrxaftertxdelay(POLL_TX_TO_RESP_RX_DLY_UUS);
dwt_setrxtimeout(RESP_RX_TIMEOUT_UUS);
tx_poll_msg[ALL_MSG_SN_IDX] = frame_seq_nb; // Индекс кадра
tx_poll_msg[ALL_MSG_TAG_IDX] = TAG_ID; // Добавление ID метки
dwt_writetxdata(sizeof(tx_poll_msg), tx_poll_msg, 0); // Записываем данные в DW1000
dwt_writetxfctrl(sizeof(tx_poll_msg), 0); // Контроль передачи
dwt_starttx(DWT_START_TX_IMMEDIATE | DWT_RESPONSE_EXPECTED);
dwt_rxenable(0);
uint32 tick1 = portGetTickCount(); // Получаем текущее время
while (!((status_reg = dwt_read32bitreg(SYS_STATUS_ID)) & (SYS_STATUS_RXFCG | SYS_STATUS_ALL_RX_ERR))) {
if ((portGetTickCount() - tick1) > 350) {
break;
}
};
GPIO_SetBits(GPIOA, GPIO_Pin_1);
if (status_reg & SYS_STATUS_RXFCG) {
dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_RXFCG | SYS_STATUS_TXFRS);
frame_len = dwt_read32bitreg(RX_FINFO_ID) & RX_FINFO_RXFLEN_MASK;
if (frame_len <= RX_BUF_LEN) {
dwt_readrxdata(rx_buffer, frame_len, 0);
}
// Проверяем, совпадает ли идентификатор метки с текущим TAG_ID
if (rx_buffer[ALL_MSG_TAG_IDX] != TAG_ID)
continue;
rx_buffer[ALL_MSG_TAG_IDX] = 0;
rx_buffer[ALL_MSG_SN_IDX] = 0;
// Сравниваем полученный кадр с ожидаемым ответом
if (memcmp(rx_buffer, rx_resp_msg, ALL_MSG_COMMON_LEN) == 0) {
uint32 final_tx_time;
/* Получаем временные метки отправки запроса и приема ответа */
poll_tx_ts = get_tx_timestamp_u64(); // Временная метка отправки запроса
resp_rx_ts = get_rx_timestamp_u64(); // Временная метка получения ответа
/* Рассчитываем время для отправки финального сообщения. См. примечание 9. */
final_tx_time = (resp_rx_ts + (RESP_RX_TO_FINAL_TX_DLY_UUS * UUS_TO_DWT_TIME)) >> 8;
dwt_setdelayedtrxtime(final_tx_time); // Устанавливаем отложенную передачу с рассчитанным временем
/* Временная метка финальной передачи — это программируемое время плюс задержка антенны */
final_tx_ts = (((uint64)(final_tx_time & 0xFFFFFFFE)) << 8) + TX_ANT_DLY;
/* Записываем все временные метки в финальное сообщение. См. примечание 10. */
final_msg_set_ts( & tx_final_msg[FINAL_MSG_POLL_TX_TS_IDX], poll_tx_ts); // Временная метка отправки запроса
final_msg_set_ts( & tx_final_msg[FINAL_MSG_RESP_RX_TS_IDX], resp_rx_ts); // Временная метка получения ответа
final_msg_set_ts( & tx_final_msg[FINAL_MSG_FINAL_TX_TS_IDX], final_tx_ts); // Временная метка финальной передачи
/* Записываем и отправляем финальное сообщение. См. примечание 7. */
tx_final_msg[ALL_MSG_SN_IDX] = frame_seq_nb; // Устанавливаем номер кадра
tx_final_msg[ALL_MSG_TAG_IDX] = TAG_ID; // Устанавливаем идентификатор метки
dwt_writetxdata(sizeof(tx_final_msg), tx_final_msg, 0); // Записываем данные для передачи
dwt_writetxfctrl(sizeof(tx_final_msg), 0); // Устанавливаем параметры контроля передачи
/* Запускаем передачу с отложенной отправкой */
dwt_starttx(DWT_START_TX_DELAYED | DWT_RESPONSE_EXPECTED);
// Ожидаем получения кадра или ошибки
while (!((status_reg = dwt_read32bitreg(SYS_STATUS_ID)) & (SYS_STATUS_RXFCG | SYS_STATUS_ALL_RX_ERR))) {
if ((portGetTickCount() - tick1) > 500) { // Если прошло более 500 мс
break; // Прерываем ожидание
}
};
/* Увеличиваем номер последовательности кадра после передачи (по модулю 256) */
if (status_reg & SYS_STATUS_RXFCG) { // Если кадр был успешно принят
/* Очищаем флаг успешного приема/передачи в регистре статуса DW1000 */
dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_RXFCG | SYS_STATUS_TXFRS);
/* Читаем принятый кадр в локальный буфер */
frame_len = dwt_read32bitreg(RX_FINFO_ID) & RX_FINFO_RXFLEN_MASK; // Получаем длину кадра
if (frame_len <= RX_BUF_LEN) { // Если длина кадра допустима
dwt_readrxdata(rx_buffer, frame_len, 0); // Читаем данные из буфера приема
}
// Проверяем идентификатор метки
if (rx_buffer[ALL_MSG_TAG_IDX] != TAG_ID)
continue; // Если не совпадает, продолжаем выполнение
rx_buffer[ALL_MSG_TAG_IDX] = 0; // Очищаем идентификатор для упрощения проверки
/* Очищаем поле номера последовательности кадра */
rx_buffer[ALL_MSG_SN_IDX] = 0;
// Проверяем, совпадают ли принятые данные с ожидаемым сообщением расстояния
if (memcmp(rx_buffer, distance_msg, ALL_MSG_COMMON_LEN) == 0) {
// Обрабатываем финальное расстояние
// final_distance = rx_buffer[10] + (float)rx_buffer[11]/100;
Anthordistance[rx_buffer[12]] += (rx_buffer[10] * 1000 + rx_buffer[11] * 10);
Anthordistance_count[rx_buffer[12]]++; // Увеличиваем счетчик измерений
{
int Anchor_Index = 0; // Индекс якоря
// Проверяем, не достигло ли количество измерений порогового значения
while (Anchor_Index < ANCHOR_MAX_NUM) {
if (Anthordistance_count[Anchor_Index] >= ANCHOR_REFRESH_COUNT) {
distance_mange(); // Вызываем функцию управления расстояниями
// Очищаем все данные после обновления
Anchor_Index = 0;
while (Anchor_Index < ANCHOR_MAX_NUM) {
Anthordistance_count[Anchor_Index] = 0; // Обнуляем счетчики измерений
Anthordistance[Anchor_Index] = 0; // Обнуляем расстояния
Anchor_Index++;
}
break; // Прерываем цикл
}
Anchor_Index++; // Переходим к следующему якорю
}
}
}
} else {
/* Очищаем все ошибки приема в регистре статуса DW1000 */
dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_ALL_RX_ERR);
}
}
} else {
/* Очищаем ошибки приема */
dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_ALL_RX_ERR);
}
/* Выполняем задержку между обменами дальностью (опционально). */
// deca_sleep(RNG_DELAY_MS); // Комментарий: задержка может быть использована для регулировки времени между запросами дальности
/* Увеличиваем номер последовательности кадра для следующего сообщения (по модулю 256). */
frame_seq_nb++; // Это необходимо для уникальной идентификации каждого отправляемого кадра
}
}
// Переменная для хранения финальной дистанции.
double final_distance = 0;
int main(void) {
// Переменные для индексов якорей и тегов (устройств).
uint8 anthor_index = 0;
uint8 tag_index = 0;
// Флаги для управления семафорами.
uint8 Semaphore_Enable = 0;
uint8 Waiting_TAG_Release_Semaphore = 0;
// Переменная для хранения длины кадров.
int8 frame_len = 0;
// Инициализация аппаратных модулей платы.
peripherals_init();
// Вывод приветственного сообщения.
printf("hello dwm1000!\r\n");
/* Сброс и инициализация чипа DW1000.
* На время инициализации тактовые сигналы DW1000 должны быть настроены на частоту кристалла.
* После инициализации скорость SPI можно увеличить для оптимальной производительности.
*/
reset_DW1000(); // Сброс линии RSTn на чипе DW1000 на низкий уровень на заданный период.
spi_set_rate_low(); // Установка низкой скорости SPI.
// Проверка инициализации DW1000.
if (dwt_initialise(DWT_LOADUCODE) == -1) {
printf("dwm1000 init fail!\r\n");
OLED_ShowString(0, 0, "INIT FAIL"); // Отображение ошибки на OLED.
// Постоянная индикация ошибки и мигание светодиода.
while (1) {
//STM_EVAL_LEDOn(LED1);
GPIO_SetBits(GPIOC, GPIO_Pin_13); // Включение светодиода.
deca_sleep(1000); // Задержка в 1 секунду.
//STM_EVAL_LEDOff(LED1);
GPIO_ResetBits(GPIOC, GPIO_Pin_13); // Выключение светодиода.
deca_sleep(1000); // Задержка в 1 секунду.
}
}
spi_set_rate_high(); // Установка высокой скорости SPI.
/* Конфигурация DW1000 с параметрами, установленными в переменной config. */
dwt_configure( & config);
dwt_setleds(1); // Включение светодиодов DW1000 для индикации работы.
/* Применение значений задержки антенны по умолчанию для передачи и приёма. */
dwt_setrxantennadelay(RX_ANT_DLY);
dwt_settxantennadelay(TX_ANT_DLY);
OLED_ShowString(0, 0, "INIT PASS"); // Отображение успешной инициализации на OLED.
printf("init pass!\r\n");
printf("AIT-BU01-DB V100 T2020-5-17\r\n"); // Вывод версии прошивки устройства.
// Координаты для якорей.
AnchorList[0].x = 0.12;
AnchorList[0].y = 0.34;
AnchorList[0].z = 0;
AnchorList[1].x = 0.25;
AnchorList[1].y = 0;
AnchorList[1].z = 0;
AnchorList[2].x = 0;
AnchorList[2].y = 0;
AnchorList[2].z = 0;
int rx_ant_delay = 32880; // Задержка антенны при приёме.
int index = 0;
extern UserSet UserSetNow; // Экземпляр структуры пользовательских настроек.
// Буфер для хранения данных, считанных из памяти.
uint16_t buff[3] = {
1,
0,
0xff
};
FLASH_ReadMoreData(USER_FLASH_BASE, buff, 3); // Чтение данных из флеш-памяти.
if (buff[0] == 1) {
UserSetNow.ANCHOR_TAG = 1; // Якорь.
} else if (buff[0] == 0) {
UserSetNow.ANCHOR_TAG = 0; // Тег.
} else {
UserSetNow.ANCHOR_TAG = 1; // По умолчанию якорь.
}
//#ifdef ANTHOR
// Если устройство является якорем.
if (UserSetNow.ANCHOR_TAG == 1) {
// Установка идентификатора якоря.
if (buff[1] >= 0 && buff[1] <= 255) {
UserSetNow.ID = buff[1];
ANCHOR_IND = UserSetNow.ID;
}
printf("device:anchor ID:%d\r\n", ANCHOR_IND);
// Инициализация массива якорей.
Anchor_Array_Init();
// Бесконечный цикл для инициации обмена данными.
OLED_ShowString(0, 0, "DS TWR ANTHOR"); // Отображение режима работы.
//OLED_ShowString(0,2,"Distance:");
//KalMan_PramInit();
ANTHOR_MEASURE(); // Вызов функции измерений для якоря.
}
//#endif
//#ifdef TAG
// Если устройство является тегом.
if (UserSetNow.ANCHOR_TAG == 0) {
// Установка идентификатора тега.
if (buff[1] >= 0 && buff[1] <= 255) {
UserSetNow.ID = buff[1];
TAG_ID = UserSetNow.ID;
MASTER_TAG = TAG_ID;
}
printf("device:TAG ID:%d\r\n", UserSetNow.ID);
if (TAG_ID == MASTER_TAG) {
OLED_ShowString(0, 0, "DS MASTER TAG:"); // Тег является главным.
} else {
OLED_ShowString(0, 0, "DS SLAVE TAG:"); // Тег является подчинённым.
}
// Установка задержки для ответа и таймаута приёма.
dwt_setrxaftertxdelay(POLL_TX_TO_RESP_RX_DLY_UUS);
dwt_setrxtimeout(RESP_RX_TIMEOUT_UUS);
//OLED_ShowString(0,2,"Distance:"); chnage by johhn
if (TAG_ID == MASTER_TAG) {
Semaphore_Enable = 1; // Разрешение работы семафора для главного тега.
Semaphore_Init(); // Инициализация семафора.
Waiting_TAG_Release_Semaphore = 0;
} else {
Semaphore_Enable = 0;
}
//Master TAG0
// Вызов функции измерений для тега.
TAG_MEASURE();
}
//#endif
}
#define Filter_N 5 // Максимальное количество фильтров, используемых в системе
const int Filter_D_set = 5; // Установка количества данных для каждого фильтра
#define Filter_D Filter_D_set // Каждому фильтру соответствует "Filter_D" данных
int Value_Buf[Filter_N][Filter_D] = { // Массив для хранения значений фильтров
0 // Инициализация всех значений в ноль
};
// Фильтрация данных с помощью усреднения значений.
int filter_index[Filter_N] = { // Индексы для отслеживания текущей позиции записи для каждого фильтра
0 // Инициализация индексов для всех фильтров
};
// Функция фильтрации данных
int filter(int input, int fliter_idx) {
char count = 0; // Счетчик для цикла
int sum = 0; // Переменная для хранения суммы значений фильтра
if (input > 0) { // Если входное значение положительное
// Запись входного значения в буфер фильтра
Value_Buf[fliter_idx][filter_index[fliter_idx]++] = input;
// Если индекс превышает размер фильтра, сбросить его в 0
if (filter_index[fliter_idx] == Filter_D) filter_index[fliter_idx] = 0;
// Подсчет суммы значений в буфере
for (count = 0; count < Filter_D; count++) {
sum += Value_Buf[fliter_idx][count]; // Суммируем значения
}
return (int)(sum / Filter_D); // Возвращаем среднее значение
} else { // Если входное значение не положительное
for (count = 0; count < Filter_D; count++) {
sum += Value_Buf[fliter_idx][count]; // Суммируем значения
}
return (int)(sum / Filter_D); // Возвращаем среднее значение
}
}
// Управление процессом измерения расстояний.
static void distance_mange(void) {
{
int Anchor_Index = 0; // Индекс для обхода якорей
while (Anchor_Index < ANCHOR_MAX_NUM) { // Цикл по всем якорям
if (Anthordistance_count[Anchor_Index] > 0) { // Проверка, есть ли измеренные расстояния
// Применение фильтрации для каждого якоря.
Anthordistance[Anchor_Index] = filter((int)(Anthordistance[Anchor_Index] / Anthordistance_count[Anchor_Index]), Anchor_Index);
}
Anchor_Index++; // Переход к следующему якорю
}
}
// Вычисление углов и отправка данных на первый якорь.
compute_angle_send_to_anthor0(Anthordistance[0], Anthordistance[1], Anthordistance[2]);
// Отображение расстояний для каждого якоря.
for (int j = 0; j < ANCHOR_MAX_NUM; j++) {
if (Anthordistance_count[j] > 0) { // Проверка, есть ли данные для якоря
sprintf(dist_str, "an%d:%3.2fm", j, (float) Anthordistance[j] / 1000); // Форматирование строки с расстоянием
printf("%s\r\n", dist_str); // Вывод расстояния в консоль
}
}
// Отображение расстояния для первого якоря на OLED
if (Anthordistance_count[0] > 0) {
sprintf(dist_str, "an0:%3.2fm", (float) Anthordistance[0] / 1000);
//printf("%s\r\n",dist_str);
OLED_ShowString(0, 2 + j * 2, dist_str); // Отображение на OLED
}
// Отображение расстояния для второго якоря на OLED
if (Anthordistance_count[1] > 0) {
sprintf(dist_str, "an1:%3.2fm", (float) Anthordistance[1] / 1000);
//printf("%s\r\n",dist_str);
OLED_ShowString(0, 4, " "); // Очистка предыдущего вывода
OLED_ShowString(0, 4, dist_str); // Отображение нового значения
}
// Отображение расстояния для третьего якоря на OLED
if (Anthordistance_count[2] > 0) {
sprintf(dist_str, "%3.2fm", (float) Anthordistance[2] / 1000);
OLED_ShowString(0, 6, " "); // Очистка предыдущего вывода
OLED_ShowString(0, 6, dist_str); // Отображение нового значения
}
}
// Координаты якорей (вышек) в системе координат (X, Y)
#define ANCHOR0_X 0.0
#define ANCHOR0_Y 0.0
#define ANCHOR1_X 5.0
#define ANCHOR1_Y 0.0
#define ANCHOR2_X 2.5
#define ANCHOR2_Y 4.33
// Макросы для количества якорей
#define ANCHOR_MAX_NUM 3
#define TAG_ID 0x01
// Прототипы функций
static void compute_angle_send_to_anthor0(float tag_x, float tag_y);
// Управление процессом измерения расстояний.
static void distance_mange(void) {
// Объявление переменных для хранения координат тега
float tag_x = 0.0; // X координата тега
float tag_y = 0.0; // Y координата тега
// Проверка, что количество измеренных расстояний до якорей больше нуля
if (Anthordistance_count[0] > 0 && Anthordistance_count[1] > 0 && Anthordistance_count[2] > 0) {
// Координаты якорей
float x1 = anchor[0].x;
float y1 = anchor[0].y;
float d1 = Anthordistance[0] / 1000.0; // Расстояние до якоря 1 в метрах
float x2 = anchor[1].x;
float y2 = anchor[1].y;
float d2 = Anthordistance[1] / 1000.0; // Расстояние до якоря 2 в метрах
float x3 = anchor[2].x;
float y3 = anchor[2].y;
float d3 = Anthordistance[2] / 1000.0; // Расстояние до якоря 3 в метрах
// Решение системы уравнений для нахождения координат тега (x, y)
float A = 2 * (x2 - x1);
float B = 2 * (y2 - y1);
float C = d1 * d1 - d2 * d2 - x1 * x1 + x2 * x2 - y1 * y1 + y2 * y2;
float D = 2 * (x3 - x2);
float E = 2 * (y3 - y2);
float F = d2 * d2 - d3 * d3 - x2 * x2 + x3 * x3 - y2 * y2 + y3 * y3;
// Вычисление координат тега
tag_x = (C - F * (A / D)) / (B - E * (A / D));
tag_y = (C - A * tag_x) / B;
// Отправка вычисленных координат тега на сервер или другим компонентам
compute_angle_send_to_anthor0(tag_x, tag_y);
} else {
// Обработка случая, когда расстояния не измерены
// Например, можно установить координаты в (0, 0) или вывести сообщение об ошибке
tag_x = 0.0;
tag_y = 0.0;
compute_angle_send_to_anthor0(tag_x, tag_y); // Отправка (0, 0)
}
// Обновление значения текущего времени (если необходимо)
// current_time = get_current_time();
}
// Отображение расстояний для якорей на OLED
for (int j = 0; j < ANCHOR_MAX_NUM; j++) {
if (Anthordistance_count[j] > 0) { // Проверка, есть ли данные для якоря
sprintf(dist_str, "an%d:%3.2fm", j, (float) Anthordistance[j] / 1000); // Форматирование строки с расстоянием
OLED_ShowString(0, 2 + j * 2, dist_str); // Отображение на OLED
} else {
OLED_ShowString(0, 2 + j * 2, "an%d: --- "); // Отображение пустого значения, если данных нет
}
}
// Функция для расчёта расстояний и углов в реальном времени
static void compute_angle_send_to_anthor0(float tag_x, float tag_y) {
float distances[ANCHOR_MAX_NUM];
for (int i = 0; i < ANCHOR_MAX_NUM; i++) {
distances[i] = calculate_distance(anchor_x[i], anchor_y[i], tag_x, tag_y); // Расстояние до всех якорей
}
// Получение углов с использованием вычисленных расстояний
float angle = compute_angle(distances[0], distances[1], distances[2]);
// Вывод угла на OLED
sprintf(dist_str, "angle: %3.2f degrees", angle);
OLED_ShowString(0, 6, " "); // Очистка предыдущего вывода
OLED_ShowString(0, 6, dist_str); // Отображение угла на OLED
// Условия для управления автомобилем в зависимости от угла
if (distances[0] > 1) { // Если расстояние больше 1 метра
if (angle > 110) {
printf("turn right\r\n"); // Поворот вправо
} else if (angle < 75) {
printf("turn left\r\n"); // Поворот влево
} else {
printf("forward\r\n"); // Движение вперед
}
} else {
printf("stay here\r\n"); // Остановка на месте
}
// Блок для отправки информации о местоположении
uint8 len = 0; // Переменная для отслеживания длины сообщения
angle_msg[LOCATION_FLAG_IDX] = 1; // Установка флага местоположения
angle_msg[LOCATION_INFO_START_IDX + (len++)] = 'm';
angle_msg[LOCATION_INFO_START_IDX + (len++)] = 'r';
angle_msg[LOCATION_INFO_START_IDX + (len++)] = 0x02; // Начало сообщения
angle_msg[LOCATION_INFO_START_IDX + (len++)] = TAG_ID; // ID тега
// Добавление расстояний в сообщение
for (int i = 0; i < ANCHOR_MAX_NUM; i++) {
angle_msg[LOCATION_INFO_START_IDX + (len++)] = (uint8)((distances[i] * 1000) & 0xFF); // Младший байт
angle_msg[LOCATION_INFO_START_IDX + (len++)] = (uint8)(((distances[i] * 1000) >> 8) & 0xFF); // Старший байт
}
// Добавление символов конца строки
angle_msg[LOCATION_INFO_START_IDX + (len++)] = '\n'; // Символ новой строки
angle_msg[LOCATION_INFO_START_IDX + (len++)] = '\r'; // Возврат каретки
angle_msg[LOCATION_INFO_LEN_IDX] = len; // Записываем фактическую длину сообщения
// Отправка данных с помощью библиотеки dwt
angle_msg[ALL_MSG_SN_IDX] = framenum; // Номер кадра
angle_msg[ALL_MSG_TAG_IDX] = TAG_ID; // Идентификатор тега
dwt_writetxdata(sizeof(angle_msg), angle_msg, 0);
dwt_writetxfctrl(sizeof(angle_msg), 0);
dwt_starttx(DWT_START_TX_IMMEDIATE);
while (!(dwt_read32bitreg(SYS_STATUS_ID) & SYS_STATUS_TXFRS)) {};
framenum++; // Увеличение номера кадра
}
static void final_msg_set_ts(uint8 * ts_field, uint64 ts) {
if (ts_field == NULL) return; // Проверка указателя
for (int i = 0; i < FINAL_MSG_TS_LEN; i++) {
// Запись младшего байта временной метки (ts) в текущий элемент массива ts_field
ts_field[i] = (uint8) ts;
// Сдвиг временной метки вправо на 8 бит, чтобы получить следующий байт
ts >>= 8;
}
float distance1 = distances[0];
float distance2 = distances[1];
float distance3 = distances[2];
// Проверка, чтобы убедиться, что расстояния больше нуля
if (distance1 > 0 && distance2 > 0 && distance3 > 0) {
float angle = compute_angle(distance1, distance2, distance3); // Вычисление угла
printf("Computed angle: %f\n", angle);
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t * file, uint32_t line) {
while (1) {}
}
#endif
// Функция, используемая для передачи символов через USART (например, при использовании printf)
PUTCHAR_PROTOTYPE {
/* Здесь можно реализовать свою версию функции fputc, которая отправляет символ через USART */
USART_ClearFlag(EVAL_COM1, USART_FLAG_TC);
USART_SendData(EVAL_COM1, (uint8_t) ch);
while (USART_GetFlagStatus(EVAL_COM1, USART_FLAG_TC) == RESET) {}
return ch;
}
for (i = 0; i <= 32; i += 4) {
выглядит подозрительно.
Я ожидал for (i = 0; i < 32; i += 4) {
.
Вы хотите выполнять цикл 33 или 32 раза?
То же самое для for (i = 0; i <= 44; i += 4) {
Ответ или решение
Проблемы в C/C++ программе для расчета расстояний между UWB DW1000 модулями
При разработке программного обеспечения на C/C++ для работы с модулями DW1000, реализующими методы трилатерации и фильтрации Калмана, важно обращать внимание на множество деталей, которые могут повлиять на корректность работы вашей программы. В приведенном коде можно выделить несколько важных аспектов, требующих внимания.
1. Целостность циклов
В вашем коде вы используете такие условия в циклах, как:
for (i = 0; i <= 32; i += 4) {
Это приведёт к выполнению цикла 33 раза, что может быть неуместно, учитывая, что вероятно, вы хотите пройтись по 8 элементам с шагом 4, то есть выполнить цикл 8 раз. Правильное условие должно быть:
for (i = 0; i < 32; i += 4) {
Аналогично, позаботьтесь о других циклах, таких как:
for (i = 0; i <= 44; i += 4) {
Их необходимо перепроверить и изменить, если требуется.
2. Очистка инициализации переменных
В некоторых случаях нужно убедиться, что переменные, такие как tag_best_solution
, Anthordistance
, и Anthordistance_count
, полностью инициализированы перед их использованием. Неправильная инициализация может привести к непредсказуемым результатам в расчетах. Используйте функцию Anchor_Array_Init
и следите за тем, чтобы любые массивы были обнулены перед их последующим использованием.
3. Обработка ошибок
Ошибка или неправильное состояние модуля могут привести к сбоям в измерениях. Рассмотрите возможность добавления более изящной обработки ошибок в функции, такие как dwt_starttx
, чтобы не только очищать флаги ошибок, но и фиксировать, когда ошибка произошла. Разделите условия и создайте разные функции для обработки случаев успешного и неуспешного получения данных.
4. Оптимизация работы с памятью
Не забывайте о важности освобождения памяти в динамических аллокациях, особенно если используются структуры данных, такие как массивы для замеров расстояний. Оцените использование стека и кучи, чтобы избежать утечек памяти.
5. Тестирование алгоритмов
Убедитесь, что ваши алгоритмы для трилатерации и фильтрации Калмана тщательно протестированы. Вам необходимо использовать тестовые данные и проверять как по времени, так и по точности, ведь ошибки на этом уровне могут приведут к значительным отклонениям в расчётах координат.
6. Эффективность кода
Проверьте, как работает ваша программа в реальных условиях. Профилирование выполнения кода будет полезно для выявления узких мест, которые могут замедлять работу вашей программы. Например, использование задержек в функции deca_sleep
может сказаться на скорости обработки данных.
Заключение
Программирование в C/C++ для систем, использующих DW1000, требует внимательного отношения ко всем аспектам приложения, от обработки данных до управления памятью. Следует также учитывать вопросы надежности и возможности расширения вашего кода.
Если вы замечаете проблемы при выполнении, полезно использовать отладчик и логирование для выявления мест, которые нуждаются в улучшении или изменении. Обратите внимание на предложенные рекомендации, и это поможет вам эффективно решать возникающие проблемы.