【问题标题】:How to use a library within a library (Arduino)如何在库中使用库 (Arduino)
【发布时间】:2021-09-15 19:52:32
【问题描述】:

我的 C++ 技能相当业余,但我正在尝试为差动驱动机器人平台编写一个库。有问题的库需要使用 PID 库和旋转编码器库。

我想利用“抽象级别”来做到这一点,而不是仅仅拥有一个庞大的“草图”,因为它使事情更容易阅读,老实说,这是面向对象编程的重点。即我可以编写一个类来控制直流电机,并为每一侧使用多个实例。 (如果我的术语有误,我深表歉意)。

基本上我在使用我的库中的第三方库时遇到了问题。

如果我要写一个使用两个第三方库的简单草图,它看起来像这样(释义)-

#include <Arduino.h>
#include <RotaryEncoder.h>
#include <PID_v1.h>
#include <L298N.h>

char A_IN1 = 8;
char A_IN2 = 9;
char A_EN = 17;
char PIN_IN1 = 4;
char PIN_IN2 = 5;

double Setpoint, Output;
double Input = 0;
double Kp=1.3, Ki=15, Kd=0.01;

RotaryEncoder encoder(PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::TWO03);
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);


void checkPosition(){
   //some code that increments the encoder value
}

void setup(){
  attachInterrupt(digitalPinToInterrupt(PIN_IN1), checkPosition, CHANGE);
  pinMode(PIN_IN1, INPUT);
  attachInterrupt(digitalPinToInterrupt(PIN_IN2), checkPosition, CHANGE);
  pinMode(PIN_IN2, INPUT);
  Setpoint = 0;
  myPID.SetSampleTime(20);
  myPID.SetMode(AUTOMATIC);
  myPID.SetOutputLimits(0,255);
}

void loop(){
  //loop code that uses PID to drive motors at appropriate speed etc etc
} 

如何将这两个库实现为自定义库?

例如两个文件 L298N.cpp 和 L298N.h?以下是我尝试过的,但没有运气。

L298N.cpp

#include "Arduino.h"
#include "L298N.h"
#include <RotaryEncoder.h>
#include <PID_v1.h>

L298N::L298N(char IN1, char IN2, char EN, char A_INT1, char A_INT2){
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(EN, OUTPUT);

  attachInterrupt(digitalPinToInterrupt(A_INT1), checkPosition, CHANGE);
  attachInterrupt(digitalPinToInterrupt(A_INT2), checkPosition, CHANGE);
  pinMode(A_INT1, INPUT);
  pinMode(A_INT2, INPUT);
    
  RotaryEncoder encoder(A_INT1, A_INT2, RotaryEncoder::LatchMode::TWO03);
  PID pid(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

  pid.SetSampleTime(20);
  pid.SetMode(AUTOMATIC);
  pid.SetOutputLimits(0,255);
    
  _IN1 = IN1;
  _IN2 = IN2;
  _EN = EN; 
}

void L298N::checkPosition(){
  encoder.tick(); // just call tick() to check the state.
}

double L298N::calculate_rpm(){
  long new_position = encoder.getPosition();
  long position_change;
  double RPM;

  if (new_position != old_position) {
    tick_time = (millis() - last_time);
    position_change = old_position - new_position;
    RPM = 1 / ((double(tick_time / position_change) * 125)/1000/60); //10041 18538 = ticks per rev, 1 rev = 42.73cm
    old_position = new_position;
    last_time = millis();   
  }
  else{
    RPM = 0.0;
  }
  delay(20); // required for dagu as encoders are shit and only pulse 125 times per rev
  return RPM;
}

void L298N::set_rpm(int rpm){
  Setpoint = rpm;//covert_vel_rpm(vel);
  Input = calculate_rpm();
  pid.Compute();
  
  if (Setpoint > 0.0){
    forwards(char(Output)); 
  }
  else{
    backwards(char(Output));
  } 
}

void L298N::forwards(char pwm){
 digitalWrite(_IN1, HIGH); 
  digitalWrite(_IN2, LOW); 
  analogWrite(_EN, pwm);
  return; 
}

void L298N::backwards(char pwm){
  digitalWrite(_IN1, LOW); 
  digitalWrite(_IN2, HIGH); 
  analogWrite(_EN, pwm);
  return;
}

L298N.h

#ifndef L298N_h
#define L298N_h

#include "Arduino.h"
#include <RotaryEncoder.h>
#include <PID_v1.h>

class L298N
{
    public:
        L298N(char IN1, char IN2, char EN, char A_INT1, char A_INT2);
        void set_rpm(int rpm);
    private:
        char _IN1, _IN2, _EN, _INT1, _INT2;
        double last_time = millis();
        double tick_time = 0;
        long old_position = 0;
        double Setpoint, Output;
        double Input = 0;
        double Kp=1.3, Ki=15, Kd=0.01;
        RotaryEncoder encoder;//(char A_INT1, char A_INT2, RotaryEncoder::LatchMode::TWO03);
        PID pid;//(double &Input, double &Output, double &Setpoint, double Kp, double Ki, double Kd, char DIRECT);  
        void checkPosition();
        double calculate_rpm();
        void forwards(char pwm);
        void backwards(char pwm);
};

#endif

test.ino

#include <L298N.h>
#include <RotaryEncoder.h>
#include <PID_v1.h>

char A_IN1 = 8;
char A_IN2 = 9;
char A_EN = 17;
char A_INT1 = 3;
char A_INT2 = 4;

L298N left(A_IN1, A_IN2, A_EN, A_INT1, A_INT2);

void setup(){
}

void loop(){
    left.set_rpm(20);
}

错误:

Arduino:1.8.15 (Linux),板:“Arduino Uno”

/home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:在构造函数中 'L298N::L298N(char, char, char, char, char)': /home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:7:67:错误:否 调用 'RotaryEncoder::RotaryEncoder()' 的匹配函数 L298N::L298N(字符 IN1,字符 IN2,字符 EN,字符 A_INT1,字符 A_INT2) ^ 在包含的文件中 /home/ubuntu/Arduino/libraries/L298N_driver/L298N.h:10:0, 来自/home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:3: /home/ubuntu/Arduino/libraries/RotaryEncoder/src/RotaryEncoder.h:39:3: 注意:候选:RotaryEncoder::RotaryEncoder(int, int, RotaryEncoder::LatchMode) RotaryEncoder(int pin1, int pin2, LatchMode 模式 = LatchMode::FOUR0); ^~~~~~~~~~~~~ /home/ubuntu/Arduino/libraries/RotaryEncoder/src/RotaryEncoder.h:39:3: 注意:候选人需要 3 个参数,提供 0 个参数 /home/ubuntu/Arduino/libraries/RotaryEncoder/src/RotaryEncoder.h:23:7: 注意:候选:constexpr RotaryEncoder::RotaryEncoder(const RotaryEncoder&) 类 RotaryEncoder ^~~~~~~~~~~~~ /home/ubuntu/Arduino/libraries/RotaryEncoder/src/RotaryEncoder.h:23:7: 注意:候选人期望 1 个参数,提供 0 个参数 /home/ubuntu/Arduino/libraries/RotaryEncoder/src/RotaryEncoder.h:23:7: 注意:候选人:constexpr RotaryEncoder::RotaryEncoder(RotaryEncoder&&) /home/ubuntu/Arduino/libraries/RotaryEncoder/src/RotaryEncoder.h:23:7: 注意:候选人期望 1 个参数,提供 0 个参数 /home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:7:67:错误:否 调用 'PID::PID()' L298N::L298N(char IN1, 字符 IN2、字符 EN、字符 A_INT1、字符 A_INT2) ^ 在包含的文件中 /home/ubuntu/Arduino/libraries/L298N_driver/L298N.h:11:0, 来自/home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:3: /home/ubuntu/Arduino/libraries/PID/PID_v1.h:24:5: 注意:候选人: PID::PID(double*, double*, double*, double, double, double, int) PID(double*, double*, double*, // * 构造函数。将 PID 链接到输入、输出和 ^~~ /home/ubuntu/Arduino/libraries/PID/PID_v1.h:24:5: 注意:候选人需要 7 个参数,提供 0 个 /home/ubuntu/Arduino/libraries/PID/PID_v1.h:20:5: 注意:候选人: PID::PID(double*, double*, double*, double, double, double, int, int) PID(double*, double*, double*, // * 构造函数。将 PID 链接到输入、输出和 ^~~ /home/ubuntu/Arduino/libraries/PID/PID_v1.h:20:5: 注意:候选人需要 8 个参数,提供 0 个 /home/ubuntu/Arduino/libraries/PID/PID_v1.h:5:7: 注意:候选人: constexpr PID::PID(const PID&) 类 PID ^~~ /home/ubuntu/Arduino/libraries/PID/PID_v1.h:5:7: 注意:候选人需要 1 个参数,提供 0 /home/ubuntu/Arduino/libraries/PID/PID_v1.h:5:7: 注意:候选人: constexpr PID::PID(PID&&) /home/ubuntu/Arduino/libraries/PID/PID_v1.h:5:7: 注意:候选 需要 1 个参数,提供 0 个参数 /home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:13:70:错误: 无效使用非静态成员函数'void L298N::checkPosition()'
attachInterrupt(digitalPinToInterrupt(A_INT1), checkPosition, CHANGE); ^ 在包含的文件中 /home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:3:0: /home/ubuntu/Arduino/libraries/L298N_driver/L298N.h:33:7:注意: 此处声明 void checkPosition(); ^~~~~~~~~~~~~ /home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:14:70: 错误: 无效使用非静态成员函数'void L298N::checkPosition()'
attachInterrupt(digitalPinToInterrupt(A_INT2), checkPosition, CHANGE); ^ 在包含的文件中 /home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:3:0: /home/ubuntu/Arduino/libraries/L298N_driver/L298N.h:33:7:注意: 此处声明 void checkPosition(); ^~~~~~~~~~~~~ exit status 1 Error compile for board Arduino Uno。

此报告将包含“显示详细输出”的更多信息 在文件 -> 首选项中启用“编译期间”选项。

任何有关如何正确执行此操作的帮助将不胜感激。它只是在一个类中使用一个类(术语可能是错误的),但这在 Python 中非常简单。

干杯!

【问题讨论】:

  • 您能否详细说明“我遇到了麻烦”?什么麻烦?
  • 它不会编译。在类中使用类的正确方法是什么?
  • 您遇到了什么编译错误,与哪一行代码有关?
  • 给我 10 分钟,我会用实际代码和编译错误更新问题
  • 我尝试了您的代码并得到了一些错误:1. 不要使用#include &lt;L298N.h&gt;,使用#include "L298N.h" 2. 找不到&lt;RotaryEncoder.h&gt;&lt;PID_v1.h&gt;。如果您的项目中有这些文件,请将括号更改为引号,就像 L298.h 一样。

标签: c++ class arduino


【解决方案1】:

你有几个问题。

  1. 你说

PID 和 RotaryEncoder 库是标准的 arduino 库

错了,这些库不是标准的,您必须使用库管理器安装它们(否则它会编译)。 它们是:

- RotaryEncoder, by Mathias Hertel
http://www.mathertel.de/Arduino/RotaryEncoderLibrary.aspx

- Arduino PID Library, by Brett Beauregard
https://github.com/br3ttb/Arduino-PID-Library

  1. 本地包含应使用引号,系统/外部包含必须使用括号:

     #include "L298N.h"
     #include <RotaryEncoder.h>
    

  1. RotaryEncoder 没有默认构造函数,因此您要么使用初始化列表初始化它,要么只删除成员变量。

  1. PID 也没有默认构造函数。

  1. 您不能将类成员函数用作中断处理程序。这个:

    attachInterrupt(digitalPinToInterrupt(A_INT1), checkPosition, CHANGE);

永远不会工作。您可能必须使用单例或类似的:

L298N left(A_IN1, A_IN2, A_EN, A_INT1, A_INT2);

// code

void int_checkPosition() {
   left.checkPosition()
}
// more code

// this may go in setup()
attachInterrupt(digitalPinToInterrupt(A_INT2), checkPosition, CHANGE);

我更改了构造函数中的代码,使其看起来像这样并编译。

// encoder and pid initialized properly
L298N::L298N(byte IN1, byte IN2, byte mEN, byte A_INT1, byte A_INT2)
 : encoder((int)A_INT1, (int)A_INT2, RotaryEncoder::LatchMode::TWO03)
 , pid(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT)
{
  piL298N::L298N(byte IN1, byte IN2, byte EN, byte A_INT1, byte A_INT2)
 : encoder((int)A_INT1, (int)A_INT2, RotaryEncoder::LatchMode::TWO03)
 , pid(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT)
{
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(EN, OUTPUT);

  // this doesn't work (use the recommendation above)
  //attachInterrupt(digitalPinToInterrupt(A_INT1), checkPosition, CHANGE);
  //attachInterrupt(digitalPinToInterrupt(A_INT2), checkPosition, CHANGE);
  pinMode(A_INT1, INPUT);
  pinMode(A_INT2, INPUT);
    
  // you don't need this. in fact, it wasn't going to work
  // because you needed it to be a class member.
  //RotaryEncoder encoder((int)A_INT1, (int)A_INT2, RotaryEncoder::LatchMode::TWO03);
  //PID pid(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

  pid.SetSampleTime(20);
  pid.SetMode(AUTOMATIC);
  pid.SetOutputLimits(0,255);
    
  _IN1 = IN1;
  _IN2 = IN2;
  _EN = EN; 
}

由您来解决中断处理程序的问题。

【讨论】:

    【解决方案2】:

    我想我明白你在问什么。 Arduino(和许多其他 IDE)有两种不同类型的库

    1) 本地库 2) 外部库

    本地库是您通过语法包含的那些:

    #include "Your_library.h"
    

    外部库是那些语法:

    #include <Your_library.h>
    

    它们之间的区别是本地库应该在项目的同一个文件夹中,这意味着如果你有一个文件夹

    Arduino/My_proyect/My_Proyect.ino

    图书馆应该在

    Arduino/My_proyect/Your_library.h

    相反,外部库位于 Arduino“库”文件夹中

    Arduino/libraries/Your_library/Your_library.h

    也许你的代码的问题是你对库使用了错误的语法类型,尝试将你想要使用的所有库(.h 和 .cpp)放在同一个文件中,并在arduino“libraries”文件夹,然后,使用外部语法包含您的库

    #include <Folder_name.h>
    

    这应该可行!

    【讨论】:

    • 问题不在于如何将库添加到代码中(尝试了您的方式和原始方式,错误相同),问题在于使用现有 OO 库的正确语法我自己的 OO 库。类中类等
    • 所以当你说类中的类时,你的意思是嵌套类。您的一个错误是 RotaryEncoder() 函数不存在或找不到,因为它是另一个类的函数。看看这个嵌套类的代码示例。 tutorialspoint.com/nested-classes-in-cplusplus
    • 如何包含文件(上面已经评论过)与主要问题几乎没有关系。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多