首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >伺服电机和电位器的问题:程序插入板时不移动

伺服电机和电位器的问题:程序插入板时不移动
EN

Stack Overflow用户
提问于 2020-05-21 03:27:36
回答 2查看 720关注 0票数 0

此代码的目的是利用电位器来转动伺服电机。当我试图将它插入到程序中时,伺服系统根本没有移动,我不知道这是我的电路板、我的布线还是我的代码的结果。如果有人能在这件事上提供帮助或提供一些帮助,我们将不胜感激。我使用的电路板是一个核心STM L476RG板和电机是一个微型SG90。

代码语言:javascript
复制
#include "mbed.h"
#include "Servo.h"
#include "iostream"

Servo myservo(D6);
AnalogOut MyPot(A1);


int main() {
    float PotReading;
    PotReading = MyPot.read();

    while(1) {
        for(int i=0; i<100; i++) {
            myservo.SetPosition(PotReading);
            wait(0.01);
        }
    }
}

此外,我所使用的代码在已发布的库伺服中具有以下代码: Servo.h

代码语言:javascript
复制
#ifndef MBED_SERVO_H
#define MBED_SERVO_H

#include "mbed.h"

/** Class to control a servo on any pin, without using pwm
 *
 * Example:
 * @code
 * // Keep sweeping servo from left to right
 * #include "mbed.h"
 * #include "Servo.h"
 * 
 * Servo Servo1(p20);
 *
 * Servo1.Enable(1500,20000);
 *
 * while(1) {
 *     for (int pos = 1000; pos < 2000; pos += 25) {
 *         Servo1.SetPosition(pos);  
 *         wait_ms(20);
 *     }
 *     for (int pos = 2000; pos > 1000; pos -= 25) {
 *         Servo1.SetPosition(pos); 
 *         wait_ms(20); 
 *     }
 * }
 * @endcode
 */

class Servo {

public:
    /** Create a new Servo object on any mbed pin
     *
     * @param Pin Pin on mbed to connect servo to
     */
    Servo(PinName Pin);

    /** Change the position of the servo. Position in us
     *
     * @param NewPos The new value of the servos position (us)
     */
    void SetPosition(int NewPos);

    /** Enable the servo. Without enabling the servo won't be running. Startposition and period both in us.
     *
     * @param StartPos The position of the servo to start (us) 
     * @param Period The time between every pulse. 20000 us = 50 Hz(standard) (us)
     */
    void Enable(int StartPos, int Period);

    /** Disable the servo. After disabling the servo won't get any signal anymore
     *
     */
    void Disable();

private:
    void StartPulse();
    void EndPulse();

    int Position;
    DigitalOut ServoPin;
    Ticker Pulse;
    Timeout PulseStop;
};

#endif

它还在与它相同的位置上有一个.cpp文件,所以如果有人需要它作为参考,我会把它作为编辑发布。我也会把电线放在以防万一

伺服系统是一个SG90

电路板的接线:

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-05-21 19:18:54

首先要了解的是伺服电机是如何工作的。从数据表中需要一个脉宽为1到2ms的50 to。脉宽决定位置,因此1ms的宽度将使伺服定位在其行程的一端,2ms的脉冲将在另一端设置位置,1.5ms将设置中心位置。

其次,您需要阅读您正在使用的Servo类的文档(在注释中)。它甚至有示例代码。首先您需要实例化一个Servo对象(您已经完成了),然后您需要通过设置它的脉冲间隔和初始脉冲宽度(或位置)来启用它:

代码语言:javascript
复制
Servo output( D6 ) ;
output.Enable( 1500, 20000 ) ; // Centre position, 50Hz

然后,如果您想读取模拟输入,显然需要一个AnalogIn对象:

代码语言:javascript
复制
AnalogOut input(A1);

然后,您需要理解的是,闭环控制系统必须不断读取其输入,以调整输出。在这里,你只读了一次电位器之前的控制回路,所以在循环中它从不改变值,所以位置不会改变。此外,您有一个完全不必要的内部循环,它来自于一个完全不同的Servo实现这里的示例代码--该示例不是一个闭环控制系统--它只是在整个范围内不停地循环伺服--这不是您在本例中试图实现的,它只是一个货物邪教编程

在闭环控制中,您可以连续地控制

代码语言:javascript
复制
         _______
        |       |
        V       |
    get-input   | <repeat>
    set-output  |
        |_______|

最后,您需要理解,在这种情况下,输入度量的单位与输出设置的单位不相同--它们需要缩放,以便将整个输入范围映射到全尺度输出范围。mbed AnalogIn类有两个读取函数;AnalogIn::read()在0.0-1.0范围内返回一个floatAnalogIn::read_u16返回一个uint16_t值0到65535。就我个人而言,我会使用整数版本,但是STM32F4部件只有一个精度FPU,因此在这种情况下,缺少硬件浮点并不是一个问题--尽管还有其他避免浮点的原因。然后,Servo::setPosition()函数根据脉冲宽度接受位置参数,并且如上所述,这与给定所建议的初始化的位置标度0到20000有关。所以你要么需要:

代码语言:javascript
复制
float set_point = input.read() ;
output.SetPosition( (int)( (set_point * 1000) + 1000 ) ; // Scale to 1 to 2ms

代码语言:javascript
复制
uint16_t set_point = input.read_u16() ;
output.SetPosition( ((set_point * 1000) >> 16) + 1000 ) ;

把所有这些加在一起(加上一些其他的改进):

代码语言:javascript
复制
#include "mbed.h"
#include "Servo.h"

int main() 
{
    // Hardware parameters
    static const int SERVO_FREQ = 50 ;                        // Hz
    static const int SERVO_PERIOD = 1000000 / SERVO_FREQ ;    // microseconds
    static const int SERVO_MIN = 1000 ;                       // 1ms in microseconds
    static const int SERVO_MAX = 2000 ;                       // 2ms in microseconds
    static const int SERVO_RANGE = SERVO_MAX  - SERVO_MIN ;

    // I/O configuration
    AnalogIn input( A1 ) ;
    Servo output( D6 ) ;
    output.Enable( SERVO_MIN, SERVO_PERIOD ) ;

    // Control loop
    for(;;)
    {
        float set_point = input.read() ;                            // 0.0 to 1.0
        output.SetPosition( set_point * SERVO_RANGE + SERVO_MIN ) ; // 1 to 2ms

        wait_us( SERVO_PERIOD ) ; // wait for one complete PWM cycle.
    }
}

定点版本将包括:

代码语言:javascript
复制
        uint16_t set_point = input.read_u16() ;                 // 0 to 2^16
        output.SetPosition( ((set_point * SERVO_RANGE) >> 16)   // 1 to 2ms
                            + SERVO_MIN ) ;

在循环中。

请注意,这不是最优雅的Servo类实现。它只不过是一个PWM类。如果将最小/最大脉冲宽度与周期一起传递给构造函数,这样您就可以给它一个零到n个设置点,而不是绝对脉冲宽度,这样会更好。这样,就可以简化一些神秘的产值计算,因为Servo类将通过适当的范围检查为您完成这一任务。实际上,如果位置参数是uint16_t,范围为0到65535,那么所有可能的输入值都是有效的,您可以直接将AnalogIn::read_u16()的输出传递给它,这样您的循环就可以包含:

代码语言:javascript
复制
output.SetPosition_u16( input.read_u16() ) ;
wait_us( SERVO_PERIOD ) ;

换句话说--获得一个更好的Servo类或者编写自己的类--在封装伺服控制专业知识方面,这对你没什么好处。

票数 1
EN

Stack Overflow用户

发布于 2020-05-21 04:23:08

即时观察

我现在看到了五个问题,从“可能的问题”到“可能的问题”。我能辨认出你照片上的别针标签,你的引脚分配似乎是正确的。假设没有奇怪的电线或电压问题:

  1. 你的模拟引脚应该是AnalogIn,而不是AnalogOut。虽然AnalogOut具有read的能力,但它用于反馈,以确保您的输出符合您的预期。现在,作为一个AnalogOut,你实际上是作为一个电压源在这个引脚上设置电压,而不是测量电压。
  2. 你不能给Servo::Enable打电话。文档告诉您如何调用Servo::Enable。一定要叫它。您甚至需要指定伺服的起始位置,这将允许您对输出和伺服进行故障排除(请参阅稍后的故障排除)。
  3. AnalogIn::read返回[0.0, 1.0]之间的float,以表示输入线路上读取的电压与系统电压(通常为5V或3.3V )之间的比率。然而,Servo::SetPosition需要一个整数来表示脉冲信号的正向部分的长度(以微秒为单位--在0到20,000之间)。如果您试图将AnalogIn::read的结果传递给Servo::SetPosition,那么您的float将被转换为0(除了只有一个罕见的情况是1)。您需要将模拟输入转换为整数输出。
  4. 现在,在您的代码中,您只在程序开始时读取模拟输入引脚的状态。您进入一个无限循环,再也不会读取这个模拟输入。你可以把那个旋钮拧到心满意足,但它永远不会影响你的节目。您需要多次阅读模拟输入。把这个移到你的循环里。
  5. 只是一个风格的东西,但你不需要内部的for循环。它除了乱七八糟的代码之外,什么也不做。如果您希望在将来的某个时候使用i的值,那么就把它留在这里,否则就放弃它。

故障排除

幸运的是,许多系统可以被认为是许多盒子(子系统)之间画出箭头。如果所有的框都正确地完成了它们的工作,并且插入了正确的下一个框,那么整个系统作为一个整体就可以工作了。您的系统如下所示:

代码语言:javascript
复制
+-----+   +-----+   +----------------+   +-------------+   +--------------------+   +-----+   +-------+
| Pot |-->| ADC |-->| AnalogIn::read |-->| Float-to-us |-->| Servo::SetPosition |-->| PWM |-->| Servo |
+-----+   +-----+   +----------------+   +-------------+   +--------------------+   +-----+   +-------+

所有这些子系统构成了整个系统。如果其中一个链接不能正常工作,那么整个链接就不能正常工作了。通常(或者至少当我们有时间时我们会这样想),我们将测试应用于系统和子系统,以确保它们根据输入产生预期的输出。如果您将输入应用到其中一个框中,并且输出是您所期望的,则该框是好的。给它一个绿色的检查标记,然后移到下一个。

对每一种情况的测试可能如下所示:

  1. Potentiometer:输入:扭转旋钮,输出:中间引脚处的电压接近系统电压(5V或3.3V),单向旋转时接近0,两端之间近似线性级数。你需要一个万用表来测量这个。
  2. 模数转换器:输入:输入引脚上的一些电压.你可以通过扭转电位器来改变这一点,一旦你确认它是有效的。输出:这有点困难,因为输出是微控制器中的寄存器。我不知道您的硬件调试环境是什么样子,所以我不能告诉您如何测量这个输出。如果您不知道如何使用调试器(但您确实应该学习如何使用硬件调试器),您可以假设这是可行的,并转移到下一个调试器。
  3. AnalogIn::read**:**输入: ADC的寄存器值。输出:有些浮动在0.0到1.0之间。我们可以同时测试模数转换器和AnalogIn::read功能,将它们作为一个子系统,以引脚电压为输入,以浮点值作为输出。同样,对于这一点,您需要一些调试功能。要么打印语句,要么是串行连接,要么是开发环境之类的。
  4. 浮点到我们的转换:输入: 0.0到1.0之间的浮点数。输出:一个介于0到20,000之间的整数,与浮点输入成比例(或反比,取决于所需的功能)。同样,由于我们正在查看变量,因此需要使用您的调试环境。
  5. Servo::SetPosition输入:一个0到20,000之间的整数,表示输出脉宽调制(PWM)信号的占空比(高周期)。产出:增加这一数字会增加观察到的占空比。减少会减少。在我们中,占空比的长度大约等于代码中的设定长度。你需要一个示波器来观察任务周期。或者,如果您的伺服工作,那么您应该看到它移动时,这一变化。
  6. 伺服:输入:脉宽调制信号。输出:一个角度的位置。一个0%的占空比应该一直到一个极端,而一个100%的占空比应该旋转到另一个极端。

结论

将您的系统视为一系列子系统。独立测试每一个。您不能期望下一个子系统“弥补”前面子系统的不足。他们都需要工作。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61926697

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档