查看: 3247|回复: 1

[项目] 【树莓派+.NET MF打造视频监控智能车】控制篇(.NET MF)

[复制链接]
  • TA的每日心情
    无聊
    2019-1-9 09:43
  • 签到天数: 6 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    发表于 2018-11-28 15:18:58 | 显示全部楼层 |阅读模式
    分享到:
    在上一篇《遥控篇》文章中,我们介绍了Sony PS2手柄信号的采集和编程,通过简单的封装,以事件的方式向我们提供按键信息。本篇文章主要介绍.NET Micro Framework系统接受到按键信息后,如何驱动小车马达和控制机械手的。

    无论是驱动小车马达还是控制机械手,都是通过输出PWM来控制的,只是控制PWM输出的方式有些不同而已,我们先介绍一下小车马达的控制。

    由于驱动马达需要相对比较大的电流,所以主芯片的IO是无法直接驱动的,中间需要连接一个驱动器。也就是说主芯片输出PWM控制驱动器,由驱动器输出大电流来驱动马达。

    一般情况下一个驱动器可以驱动两路马达,而驱动一个马达一般需要两路信号,通过控制两路PWM的输出,来控制马达的转速和方向。我们这款小车选用的是一个带光电隔离,高功率的一个驱动器,一路马达,需要三路IO控制,其中2路是控制方向,一路输出PWM控制小车的转速。
    1.jpg

    一个驱动器模块,需要4个GPIO(控制方向)2路PWM,加上一路3V3和GND,共8路,我们采用标准.NET Gadgeteer接口进行连接(10个pin:1个3V3,1个5V,1个GND,7个GPIO通道),可以直接插入凌霄开发板上的两个.NET Gadgeteer接口上,接线显的简单直接(上图所示的两个扁平电缆就是)。

    机械手控制就更为容易了,一个舵机三根线,电源(5V),地和信号线(PWM),三个舵机,一共需要3路PWM输出控制。注意电源也需要特别提供。PWM由凌霄开发板IO直接输出。
    2.jpg

    下面介绍一下.NET Micro Framework的PWM接口函数类。
    1. public class PWM : IDisposable

    2.     {

    3.         public PWM(Cpu.PWMChannel channel, double frequency_Hz, double dutyCycle, bool invert);

    4.         public PWM(Cpu.PWMChannel channel, uint period, uint duration, PWM.ScaleFactor scale, bool invert);



    5.         public uint Duration { get; set; }

    6.         public double DutyCycle { get; set; }

    7.         public double Frequency { get; set; }

    8.         public uint Period { get; set; }

    9.      

    10.         public void Start();

    11.        public void Stop();

    12.       

    13.       //… 省略一些非主要函数

    14.    }
    复制代码
    两个构造函数,分别介绍一下。

    PWM(Cpu.PWMChannel channel, double frequency_Hz, double dutyCycle, bool invert);

    channel – 通道,不同的系统,支持的通道个数不同,比如凌霄系统支持16路,应该算比较多的。

    frequency_Hz – 频率,单位是Hz,发出脉冲的频率值。

    dutyCycle – 占空比, 一个0~1之间的数,表示一个周期中,高电平持续时间和整个周期的比值。

    Invert – 信号翻转,高低电平翻转切换,一般底层都没有处理该参数,所以一般设置为false。



    public PWM(Cpu.PWMChannel channel, uint period, uint duration, PWM.ScaleFactor scale, bool invert);

    channel – 通道。

    period – 周期。单位和scale的选项一致。

    duration – 高电平持续时间,单位和周期一致。

    scale – 周期的时间单位,可以是毫秒、微秒、纳秒,建议选择微秒。

    Invert – 信号翻转。



    第一个构造函数,一般控制马达用,参数设置显的比较直观。频率可以是1K~250KHz(建议10K左右),通过设置占空比的大小(0就是停止,1就是全速),来进行调速。

    第二个构造函数,适合控制舵机用,舵机典型的控制曲线如下:
    3.jpg
    F就是所谓的周期了,如果我们设置scale为微秒,则可以直接设置为20000,所谓的脉宽就是duration的值,你可以设置为1000~2000之间(不同舵机,这个区域的值会有不同,请根据实际进行调整)。

    有了以上的介绍,我们就可以很容易地完成马达驱动和舵机控制了。

    A.驱动马达

    马达驱动控制参数定义:   
    1. static PWM[] motor_pwm = new PWM[4];

    2.     static double[] frequency = new double[] { 10000, 10000, 10000, 10000 };        

    3.     static double[] dutyCycle = new double[] { 0, 0, 0, 0};                        

    4.     static bool[] states1 = new bool[] { true, false, true, false, true, false, true, false };

    5.     static bool[] states2 = new bool[] { false, true, false, true, false, true, false, true };

    6.     static OutputPort[] In = new OutputPort[8];
    复制代码
    马达驱动初始化:
    1.   //初始化马达控制

    2.     //方向IO

    3.     Cpu.Pin[] pins = new Cpu.Pin[] { Mainboard.Gadgeteer.Socket2.Pin4, Mainboard.Gadgeteer.Socket2.Pin5, Mainboard.Gadgeteer.Socket2.Pin6, Mainboard.Gadgeteer.Socket2.Pin7,

    4.                                         Mainboard.Gadgeteer.Socket1.Pin4, Mainboard.Gadgeteer.Socket1.Pin5, Mainboard.Gadgeteer.Socket1.Pin6, Mainboard.Gadgeteer.Socket1.Pin7};           

    5.     for (int i = 0; i < In.Length; i++)

    6.     {

    7.         In[i] = new OutputPort(pins[i], false);

    8.     }

    9.     //马达速度PWM输出

    10.     Cpu.PWMChannel[] motor_chanels = new Cpu.PWMChannel[] { Mainboard.PWM.Channel13, Mainboard.PWM.Channel14, Mainboard.PWM.Channel2, Mainboard.PWM.Channel3 };

    11.     for (int i = 0; i < motor_pwm.Length; i++)

    12.     {

    13.         motor_pwm[i] = new PWM(motor_chanels[i], frequency[i], dutyCycle[i], false);

    14.         motor_pwm[i].Start();

    15.     }

    复制代码
    马达控制:

    在Sony PS2的事件代码中,我们填写如下代码:
    1. static void ps2_Click(object sender, PS2.ButtonArgs e)

    2.     {

    3. if(e.key == PS2.Key.RRocker)

    4.         {

    5.             PS2 ps2 = (PS2)sender;

    6.             PS2.ButtonArgs button = ps2.GetButton(PS2.Key.L2);

    7.             if (button.state == 1) //L2按下

    8.             {

    9.                 byte[] buffer = new byte[] { 0xAA, (byte)e.x, (byte)e.y, 0x55 };

    10.                 piPort.Write(buffer, 0, 4);

    11.                 piPort.Flush();



    12.                 //左右旋转

    13.                 steering_pwm[3].Duration = (UInt32)(durations[3] + (128 - e.x) * 5);

    14.                 //上下旋转

    15.                 steering_pwm[4].Duration = (UInt32)(durations[4] + (128 - e.y) * 5);



    16.             }

    17.             Else  //L2抬起

    18.             {

    19.                 //小车运动

    20.                 UInt32[] values = new UInt32[4];

    21.                 UInt32 x = (UInt32)(System.Math.Abs(e.x - 128));

    22.                 UInt32 y = (UInt32)(System.Math.Abs(e.y - 128));

    23.                 for (int i = 0; i < values.Length; i++) values[i] = y;



    24.                 if (e.y < 128)

    25.                 {

    26.                     //前进

    27.                     for (int i = 0; i < In.Length; i++) In[i].Write(states2[i]);

    28.                     //拐弯

    29.                     if (x > 30)

    30.                     {

    31.                         if (e.x < 128)

    32.                         {

    33.                             values[2] = x;

    34.                             values[3] = x;

    35.                         }

    36.                         else

    37.                         {

    38.                             values[0] = x;

    39.                             values[1] = x;

    40.                         }

    41.                     }

    42.                 }

    43.                 else

    44.                 {

    45.                     //后退

    46.                     for (int i = 0; i < In.Length; i++) In[i].Write(states1[i]);

    47.                     //拐弯

    48.                     if (x > 30)

    49.                     {

    50.                         if (e.x < 128)

    51.                         {

    52.                             values[0] = x;

    53.                             values[1] = x;

    54.                         }

    55.                         else

    56.                         {

    57.                             values[2] = x;

    58.                             values[3] = x;

    59.                         }

    60.                     }

    61.                 }

    62.                 //设置占空比

    63.                 for (int i = 0; i < motor_pwm.Length; i++) motor_pwm[i].DutyCycle = (values[i] / 128.0);

    64.             }

    65.         }

    66.     }      
    复制代码

    B、驱动舵机

    舵机参数定义:
    1. static PWM[] steering_pwm = new PWM[5];

    2.     static UInt32[] periods = new UInt32[] { 20000, 20000, 20000, 20000, 20000 };//周期        static UInt32[] durations = new UInt32[] { 1390, 1500, 1390, 1550, 1420 };   //脉宽
    复制代码

    舵机初始化:
    1. Cpu.PWMChannel[] steering_chanels = new Cpu.PWMChannel[] { Mainboard.PWM.Channel8, Mainboard.PWM.Channel9, Mainboard.PWM.Channel6, Mainboard.PWM.Channel0, Mainboard.PWM.Channel4 };

    2.     for (int i = 0; i < steering_pwm.Length; i++)

    3.     {

    4.         steering_pwm[i] = new PWM(steering_chanels[i], periods[i], durations[i], PWM.ScaleFactor.Microseconds, false);

    5.         steering_pwm[i].Start();

    6.     }
    复制代码

    舵机控制:
    1. static void ps2_Click(object sender, PS2.ButtonArgs e)

    2.     {

    3.        if (e.key == PS2.Key.LRocker) //左摇杆事件

    4.         {

    5.             //控制机械臂左右旋转

    6.             steering_pwm[0].Duration = (UInt32)(durations[0] + (128 - e.x) * 5);

    7.             //控制机械臂上下旋转

    8.             steering_pwm[1].Duration = (UInt32)(durations[1] + (128 - e.y) * 5);

    9.         }

    10.         else if (e.key == PS2.Key.R2)  //按下右R2键

    11.         {

    12.             //打开钳子

    13.             value += 10; if (value > 255) value = 255;

    14.             steering_pwm[2].Duration = (UInt32)(durations[2] + (value - 128) * 5);

    15.         }

    16.         else if (e.key == PS2.Key.R1)  //按下右R1键

    17.         {  

    18.             //闭合钳子

    19.             value -= 10;if (value < 0) value = 0;

    20.             steering_pwm[2].Duration = (UInt32)(durations[2] + (value - 128) * 5);

    21.         }

    22.      }
    复制代码

    C、视频演示


    本文作者 叶帆,转载自cnblogs













    回复

    使用道具 举报

  • TA的每日心情

    2023-7-25 22:49
  • 签到天数: 385 天

    连续签到: 1 天

    [LV.9]以坛为家II

    发表于 2018-11-29 10:55:06 | 显示全部楼层
    谢谢分享。 null9.png null8.png

    null7.png null6.png

    null5.png null4.png

    null3.png null2.png

    null1.png null0.png


    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /2 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-3-19 16:43 , Processed in 0.142692 second(s), 21 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2020, Tencent Cloud.