Ошибка компиляции скетча для преобразования класса/библиотеки.
Я пытаюсь взять скетч и превратить его в класс для создаваемой мной библиотеки, но у меня возникают проблемы с компиляцией.
Это не дубликат, как предлагалось. как я объясняю в комментарии и своем ответе. Спасибо всем за вашу помощь - поскольку я новичок в создании библиотек - решил, что попробую что-нибудь правильно для разнообразия :)
Вот исходный скетч: Добавлен здесь
#include <ros.h>
#include <sensor_msgs/BatteryState.h>
#define K 0.00472199
#define CELLS 6
#define MAX_CELLS 12
double cell_const[MAX_CELLS] =
{
1.0000,
2.1915,
2.6970,
4.1111,
4.7333,
6.6000,
6.6000,
7.8293,
8.4667,
9.2353,
11.0000,
11.0000
};
ros::NodeHandle nh;
sensor_msgs::BatteryState batt_state;
ros::Publisher batteryState("battery_state", &batt_state);
void setup()
{
// Инициализируем узел ROS.
nh.initNode();
nh.advertise(batteryState);
// Заполняем параметры батареи.
batt_state.design_capacity = 2200; // мАч
batt_state.power_supply_status = 2; // разрядка
batt_state.power_supply_health = 0; // неизвестный
batt_state.power_supply_technology = 3; // ЛиПо
batt_state.present = 1; // батарея присутствует
batt_state.location = "Crawler"; // местоположение юнита
batt_state.serial_number = "ABC_0001"; // серийный номер устройства
batt_state.cell_voltage = new float[CELLS];
}
void loop()
{
// Заряд батареи.
double battVoltage = 0.0;
double prevVoltage = 0.0;
// Заполняем сообщение о состоянии батареи.
for (int i = 0; i < CELLS; i++)
{
// Чтение необработанного напряжения с аналогового контакта.
double cellVoltage = analogRead(i) * K;
// Масштабируем показания до полного напряжения.
cellVoltage *= cell_const[i];
double tmp = cellVoltage;
// Изолируем текущее напряжение ячейки.
cellVoltage -= prevVoltage;
battVoltage += cellVoltage;
prevVoltage = tmp;
// Установить текущее напряжение ячейки в сообщение.
batt_state.cell_voltage[i] = (float)cellVoltage;
// Проверяем, подключена ли батарея.
if (batt_state.cell_voltage[i] >= 2.0)
batt_state.present = 1;
else
batt_state.present = 0;
}
// Обновляем состояние батареи.
batt_state.voltage = (float)battVoltage;
if (batt_state.voltage > CELLS * 4.2)
batt_state.power_supply_health = 4; // перенапряжение
else if (batt_state.voltage < CELLS * 3.0)
batt_state.power_supply_health = 3; // мертвый
else
batt_state.power_supply_health = 1; // хороший
// Публикация данных в ROSSERIAL.
batteryState.publish( &batt_state );
nh.spinOnce();
delay(1000);
}
Вот мой заголовочный файл:
#ifndef battery_h
#define battery_h
#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include <ros.h>
#include <sensor_msgs/BatteryState.h>
#define K 0.00472199
#define CELLS 6
#define MAX_CELLS 12
#define CRITICAL 0.30
double cell_const[MAX_CELLS] =
{
1.0000, 2.1915, 2.6970, 4.1111,
4.7333, 6.6000, 6.6000, 7.8293,
8.4667, 9.2353, 11.0000, 11.0000
};
ros::NodeHandle nh;
sensor_msgs::BatteryState batt_state;
ros::Publisher batteryState("/battery/info", &batt_state);
class Battery
{
private:
void updateBatteryHealth();
double battVoltage;
double prevVoltage;
public:
void begin();
void updateBatteryState();
};
#endif
Это мой файл cpp:
#include "battery.h"
void Battery::begin(){
// Запускаем узел ROS и устанавливаем параметры.
nh.initNode();
// Настройка издателей/подписчиков.
nh.advertise(batteryState);
// Заполняем параметры батареи.
batt_state.design_capacity = 2200; // мАч
batt_state.power_supply_status = 2; // разрядка
batt_state.power_supply_health = 0; // неизвестный
batt_state.power_supply_technology = 3; // ЛиПо
batt_state.present = 1; // батарея присутствует
batt_state.location = "Crawler"; // местоположение юнита
batt_state.serial_number = "ABC_0001"; // серийный номер устройства
batt_state.cell_voltage = new float[CELLS]; // здоровье отдельной клетки
}
void Battery::updateBatteryState() {
battVoltage = 0.0;
prevVoltage = 0.0;
// Сброс состояния источника питания.
batt_state.power_supply_health = 0;
// Заполняем сообщение о состоянии батареи.
for (int i = 0; i < CELLS; i++)
{
// Чтение необработанного напряжения с аналогового контакта.
double cellVoltage = analogRead(i) * K;
// Масштабируем показания до полного напряжения.
cellVoltage *= cell_const[i];
double tmp = cellVoltage;
// Изолируем текущее напряжение ячейки.
cellVoltage -= prevVoltage;
battVoltage += cellVoltage;
prevVoltage = tmp;
// Установить текущее напряжение ячейки в сообщение.
batt_state.cell_voltage[i] = (float)cellVoltage;
// Проверяем, подключена ли батарея.
if (batt_state.cell_voltage[i] >= 2.0)
{
if (batt_state.cell_voltage[i] <= 3.2)
batt_state.power_supply_health = 5; // Неуказанный сбой.
batt_state.present = 1;
}
else{
batt_state.present = 0;
}
}
};
void Battery::updateBatteryHealth() {
// Обновляем состояние батареи.
if (batt_state.present)
{
batt_state.voltage = (float)battVoltage;
float volt = batt_state.voltage;
float low = 3.0 * CELLS;
float high = 4.2 * CELLS;
batt_state.percentage = constrain((volt - low) / (high - low), 0.0, 1.0);
}
else
{
batt_state.voltage = 0.0;
batt_state.percentage = 0.0;
}
// Обновить состояние источника питания, если не удалось.
if (batt_state.power_supply_health == 0 && batt_state.present)
{
if (batt_state.voltage > CELLS * 4.2)
batt_state.power_supply_health = 4; // перенапряжение
else if (batt_state.voltage < CELLS * 3.0)
batt_state.power_supply_health = 3; // мертвый
else
batt_state.power_supply_health = 1; // хороший
}
// Публикация данных в ROSSERIAL.
batteryState.publish( &batt_state );
nh.spinOnce();
};
это ошибка, которую я получаю, просто вызывая функцию Battery::begin в моем основном классе:
libraries/KBROSRobot/KBROSRobot.cpp.o (symbol from plugin): In function `KBROSRobot::KBROSRobot()':
(.text+0x0): multiple definition of `nh'
sketch/rob_test.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
libraries/KBROSRobot/KBROSRobot.cpp.o (symbol from plugin): In function `KBROSRobot::KBROSRobot()':
(.text+0x0): multiple definition of `batt_state'
sketch/rob_test.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
libraries/KBROSRobot/KBROSRobot.cpp.o (symbol from plugin): In function `KBROSRobot::KBROSRobot()':
(.text+0x0): multiple definition of `batteryState'
sketch/rob_test.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
libraries/KBROSRobot/KBROSRobot.cpp.o (symbol from plugin): In function `KBROSRobot::KBROSRobot()':
(.text+0x0): multiple definition of `cell_const'
sketch/rob_test.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
/var/folders/m4/lq13_j5j23qc0l2wp8dqmt4h0000gp/T//cczNyzqC.ltrans1.ltrans.o: In function `begin':
/Users/keith.brown/Documents/Arduino/libraries/KBROSRobot/KBROSRobot.cpp:9: undefined reference to `Battery::begin()'
collect2: error: ld returned 1 exit status
exit status 1
@rcpilotp51, 👍-1
Обсуждение2 ответа
Лучший ответ:
Не определяйте глобальные переменные в заголовочном файле. Только объявляйте их, используя внешнюю ссылку. Затем определите их в файле реализации
Заголовок (.h):
extern ros::NodeHandle nh;
Реализация (.cpp):
ros::NodeHandle nh;
Старайтесь свести использование глобальных переменных к минимуму. Если они вам не нужны вне библиотеки, объявите их в файле реализации и пометьте как статические.
Или еще лучше: инкапсулируйте их внутри класса.
Причина появления этих ошибок заключается в том, что каждый файл реализации компилируется отдельно. У вас есть два файла реализации, которые включают файл заголовка, поэтому файл заголовка компилируется дважды. В результате появятся две глобальные переменные с именем nh
. На этапе связывания компоновщик пытается объединить скомпилированные версии KBROSRobot.cpp
и rob_test.ino.cpp
(они называются объектными файлами, .o
). Компоновщик не может узнать, какая переменная какая, поскольку обе они определены дважды, поэтому он выдает ошибку.
Несколько объявлений — это нормально, поскольку они просто сообщают компилятору, что «где-то есть определение», и компоновщик рано или поздно обнаружит это определение. Вот почему объявление с внешней связью решает вашу проблему.
Множественные определения не допускаются (в большинстве случаев).
я попробую вышеописанное - как вы думаете, я правильно конвертировал эскиз? должен ли я сделать что-то еще по-другому?, @rcpilotp51
@ rcpilotp51 Вы должны сделать то же самое для всех определений в заголовочном файле. Я не рассматривал остальную часть вашего кода подробно., @tttapa
я получаю сообщение об ошибке по поводу extern: предупреждение: 'batteryState' инициализирован и объявлен 'extern'
extern ros::Publisher BatteryState("/battery/info", &batt_state);
, @rcpilotp51
@rcpilotp51 удалите инициализатор в заголовочном файле (т.е. просто используйте extern ros::Publisher BatteryState;
). Добавьте инициализатор в файл реализации., @tttapa
ок, избавился от множества ошибок, теперь просто осталось: var/folders/m4/lq13_j5j23qc0l2wp8dqmt4h0000gp/T//ccB5PTqK.ltrans0.ltrans.o: В функции
begin':
/Users/keith.brown/Documents/Arduino/libraries/KBROSRobot/KBROSRobot.cpp:9: неопределенная ссылка на Battery::begin()'
Collect2: ошибка: ld вернул 1 статус выхода
статус выхода 1
, @rcpilotp51
если указанная выше переменная является «внешней», следует ли мне определить ее в разделе «private» var? @tttapa, @rcpilotp51
@ rcpilotp51 Вы не определили void Battery::begin()
в своем файле реализации. Нет, переменные класса и члена не должны иметь пометку extern., @tttapa
он определен в классе Battery .h и реализован как Battery::begin() в cpp-файле void Begin();
в заголовке. @tttapa, @rcpilotp51
@ rcpilotp51 Компилятор, похоже, не согласен. Проверьте точки с запятой и скобки. Также имейте в виду, что он использует версию из папки «libraries»., @tttapa
Я редактирую прямо в папке библиотеки. насколько я могу судить, оно там.... есть еще предложения? @tttapa, @rcpilotp51
Я также обнаружил, что все остальные классы в библиотеке должны находиться в папке utility
, иначе они не будут компилироваться. Отвечаю на мой комментарий выше. Спасибо @tttapa за вашу помощь.
Теперь мне осталось грамотно запрограммировать робота, что, к сожалению, субъективно... ;)
Спецификации структуры папок библиотеки: https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5:-Library-спецификация, @Juraj
- Встроенная цифровая карта для управления роботом
- Как управлять 4 двигателями постоянного тока с помощью Arduino?
- Аккумуляторы LiPo с контроллером L298N
- Наилучший метод проектирования схемы с 20 кнопками
- Сообщение об ошибке: "exit status 1 expected initializer before 'void'."
- Как сгенерировать файлы .a и .so для добавления в проект arduino
- Как использовать библиотеку Modbus RTU функционального кода 6
- Как подключить несколько акселерометров MMA8451 (протокол I2C) с помощью одного Arduino Mega
Возможный дубликат Ошибка множественного определения при связывании прошивки.elf, @KIIV
@KIIV, как это дубликат? это был беспорядок с защитой заголовков и расширениями файлов. здесь компоновщик не может найти функцию, которая существует в исходном коде. пожалуйста, объясни, если ты видишь что-то, чего я не вижу, @Juraj
@Juraj В ответе я попытался рассказать, как объявить что-то в заголовке и определить это в исходном файле, чтобы это не приводило к множественным определениям., @KIIV
@KIIV, извини, да, но ответ tttapa касается и этого. но последняя ошибка, возможно, является чем-то другим
неопределенной ссылкой на Battery::begin()
, @Juraj@Juraj Правда, правда. Неопределенная батарея::begin — это странно. Я не вижу в вопросе ничего, что объясняло бы, почему он терпит неудачу., @KIIV
@Juraj и KIIV проверяют мой ответ ниже. У меня каждый класс находился в именованных папках /sensors/battery... Drive/motors... /utility/statusLights... каждый другой класс нужно было поместить в папку утилит... поэтому класс Battery не компилировался., @rcpilotp51