• 双轮云台小车实现追踪彩色目标功能


    1. 功能说明

         在R216a样机上安装一个摄像头,本文示例将实现双轮小车通过二自由度云台自主寻找彩色目标的功能。

     2. 结构说明

          R216a样机主要是由一个 双轮小车 和一个 2自由度云台 组合而成。

    3. 电子硬件

          在这个示例中,我们采用了以下硬件,请大家参考:

    主控板

    Basra主控板(兼容Arduino Uno)

    扩展板

    Bigfish2.1扩展板

    电池7.4V锂电池
    通信2510通信转接板
    WiFi路由器
    其它摄像头
    配置 OpenCV的Visual Studio 2015.net环境 的计算机一台

     电路连接说明:

          ① 将2510通信转接板连接到Bigfish扩展板的扩展坞上面;

          ② 用3根母对母杜邦线将2510通信转接板与WiFi路由器连接起来:GND-GND、RX-RX、TX-TX;

          ③ 找到1根USB线,一端连接到2510通信转接板接口上,另一端连接到WiFi路由器USB接口上;

          ④ 将摄像头线连接到WiFi路由器接口上。

    4. 功能实现

        实现思路:实现双轮云台小车追踪蓝色小球。

    4.1 工作原理

        ① 摄像头采集图像信息;

        ② 通过WiFi将信息传递给PC端(VS2015配置的OpenCV环境);

        ③ 使用OpenCV的目标颜色跟踪camshift算法取得目标物体的中心点坐标;

        ④ 采用九宫格方式对摄像显示图像进行分割;

        ⑤ 确定目标物体在显示图像的所处九宫格位置;

        ⑥ 如果目标图像超出九宫格位置的中心,调整摄像头矫正偏移使目标物体在屏幕中心位置;

        ⑦ 调整摄像头需要上位机通过WiFi给下位机发送矫正指令,下位机需要接收信号,并且让安装了摄像头的智能小车做出相应的矫正动作,

             如果丢失目标,智能车上的云台会转动以寻找目标。

    4.2 示例程序

         编程环境:Arduino 1.8.19

         ① 下位机例程

         将参考例程(example.ino)下载到主控板,打开路由器,待路由器完成启动后,将路由器与主控板的TX、RX串口连接,同时将PC连接至路由器WIFI网络。下位机接收上位机处理的图像信息结果控制云台相应运动,云台跟随目标物体运动。

    1. /*------------------------------------------------------------------------------------
    2. 版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.
    3. Distributed under MIT license.See file LICENSE for detail or copy at
    4. https://opensource.org/licenses/MIT
    5. by 机器谱 2023-04-24 https://www.robotway.com/
    6. ------------------------------*/
    7. /*
    8. car_exapmle
    9. 2018/06/07
    10. ---------------------
    11. Motor:
    12. left: 9,5
    13. right: 10,6
    14. Servo:
    15. bottom:4
    16. top:7
    17. */
    18. #include
    19. #define BOTTOM_SERVO_MIN 60
    20. #define BOTTOM_SERVO_MAX 150 //175
    21. #define BOTTOM_SERVO_MIDDLE 85
    22. #define TOP_SERVO_MIN 85
    23. #define TOP_SERVO_MAX 175
    24. const String CMD_LEFT = "L";
    25. const String CMD_RIGHT = "R";
    26. const String CMD_STOP = "S";
    27. const String CMD_FORWARD = "F";
    28. const String CMD_LOST = "N";
    29. Servo myServo[2];
    30. int port0 = 4;
    31. int port1 = 7;
    32. int servo_value[2] = {85, 105};
    33. int servo_move_angle = 1;
    34. void setup() {
    35. Serial.begin(9600);
    36. pinMode(5,OUTPUT);
    37. pinMode(6,OUTPUT);
    38. pinMode(9,OUTPUT);
    39. pinMode(10,OUTPUT);
    40. ServoInit();
    41. delay(1000);
    42. }
    43. void loop() {
    44. String data = SerialRead();
    45. if(data == CMD_STOP)
    46. {
    47. Stop();
    48. }
    49. else if(data == CMD_FORWARD)
    50. {
    51. Forward();
    52. }
    53. else if(data == CMD_LEFT)
    54. {
    55. Left();
    56. delay(60);
    57. Stop();
    58. }
    59. else if(data == CMD_RIGHT)
    60. {
    61. Right();
    62. delay(60);
    63. Stop();
    64. }
    65. else if(data == CMD_LOST)
    66. {
    67. FindObject();
    68. }
    69. }
    70. String SerialRead()
    71. {
    72. String str = "";
    73. while(Serial.available())
    74. {
    75. str += char(Serial.read());
    76. }
    77. return str;
    78. }
    79. int Angle2Pwm(int n)
    80. {
    81. return map(n, 0, 180, 500, 2500);
    82. }
    83. void ServoInit()
    84. {
    85. myServo[0].attach(port0);
    86. myServo[1].attach(port1);
    87. for(int i=0;i<2;i++)
    88. {
    89. myServo[i].write(Angle2Pwm(servo_value[i]));
    90. }
    91. }
    92. void FindObject()
    93. {
    94. const int times = 30;
    95. int dir = 0;
    96. for(;;)
    97. {
    98. String data = SerialRead();
    99. if(data != CMD_LOST && data != "")
    100. {
    101. if(servo_value[0] <= BOTTOM_SERVO_MIDDLE)
    102. {
    103. dir = 1; // turn left
    104. for(int i=0;i0] - BOTTOM_SERVO_MIDDLE);i++)
    105. {
    106. servo_value[0] += 1;
    107. myServo[0].write(Angle2Pwm(servo_value[0]));
    108. delay(times);
    109. }
    110. }
    111. else if(servo_value[0] > BOTTOM_SERVO_MIDDLE)
    112. {
    113. dir = 2; // turn right
    114. for(int i=0;i0] - BOTTOM_SERVO_MIDDLE);i++)
    115. {
    116. servo_value[0] -= 1;
    117. myServo[0].write(Angle2Pwm(servo_value[0]));
    118. delay(times);
    119. }
    120. }
    121. break;
    122. }
    123. if(servo_value[0] <= BOTTOM_SERVO_MIN)
    124. {
    125. servo_move_angle = 1;
    126. servo_value[0] = BOTTOM_SERVO_MIN;
    127. }
    128. else if(servo_value[0] >= BOTTOM_SERVO_MAX)
    129. {
    130. servo_move_angle = -1;
    131. servo_value[0] = BOTTOM_SERVO_MAX;
    132. }
    133. servo_value[0] += servo_move_angle;
    134. myServo[0].write(Angle2Pwm(servo_value[0]));
    135. delay(times);
    136. }
    137. if(dir == 1)
    138. {
    139. Left();
    140. delay(500);
    141. Stop();
    142. }
    143. else if(dir == 2)
    144. {
    145. Right();
    146. delay(500);
    147. Stop();
    148. }
    149. }
    150. void Forward()
    151. {
    152. analogWrite(5,120);
    153. analogWrite(6,0);
    154. analogWrite(9,120);
    155. analogWrite(10,0);
    156. }
    157. void Left()
    158. {
    159. analogWrite(5,105);
    160. analogWrite(6,0);
    161. analogWrite(9,0);
    162. analogWrite(10,105);
    163. }
    164. void Right()
    165. {
    166. analogWrite(5,0);
    167. analogWrite(6,105);
    168. analogWrite(9,105);
    169. analogWrite(10,0);
    170. }
    171. void Stop()
    172. {
    173. digitalWrite(5,HIGH);
    174. digitalWrite(6,HIGH);
    175. digitalWrite(9,HIGH);
    176. digitalWrite(10,HIGH);
    177. }

    ② 上位机例程

       下面提供一个可以实现双轮智能小车追踪蓝色小球的参考例程(MainWindow.xaml.cs),大家可参考演示视频完成该实验。

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. using System.Windows;
    7. using System.Windows.Controls;
    8. using System.Windows.Data;
    9. using System.Windows.Documents;
    10. using System.Windows.Input;
    11. using System.Windows.Media;
    12. using System.Windows.Media.Imaging;
    13. using System.Windows.Navigation;
    14. using System.Windows.Shapes;
    15. using System.Windows.Forms;
    16. using System.Runtime.InteropServices;
    17. using System.Threading;
    18. using System.Net;
    19. using System.Net.Sockets;
    20. namespace Project
    21. {
    22. ///
    23. /// 云台跟踪
    24. ///
    25. public partial class MainWindow : Window
    26. {
    27. //导入 camshift.dll 动态链接库
    28. [DllImport("Camshift_DLL.dll", CharSet = CharSet.Ansi)]
    29. public static extern void Camshift([MarshalAs(UnmanagedType.LPStr)]string ip_address, //图片或视频地址
    30. ref int xpos, //检测框中心X坐标
    31. ref int ypos, //检测框中心Y坐标
    32. ref int td); //检测区域对角线长度
    33. //定义窗口大小
    34. int cap_w = 320, cap_h = 240;
    35. //跟踪物体中心 x, y 坐标值
    36. int x = 0, y = 0, d = 0;
    37. //定义命令变量
    38. string CMD_FORWARD = "", CMD_TURN_LEFT = "", CMD_TURN_RIGHT = "", CMD_STOP = "", CMD_LOST = "";
    39. //结构体
    40. public struct Boundaries
    41. {
    42. public int x_left;
    43. public int x_right;
    44. public int y_up;
    45. public int y_down;
    46. public int d_min;
    47. public int d_max;
    48. }
    49. Boundaries boundaries = new Boundaries();
    50. public MainWindow()
    51. {
    52. InitializeComponent();
    53. }
    54. private void Window_Loaded(object sender, RoutedEventArgs e)
    55. {
    56. GetIni();
    57. SetPosition();
    58. CmdInit();
    59. StructInit();
    60. }
    61. //变量初始化
    62. private void CmdInit()
    63. {
    64. CMD_FORWARD = "F";
    65. CMD_TURN_LEFT = "L";
    66. CMD_TURN_RIGHT = "R";
    67. CMD_STOP = "S";
    68. CMD_LOST = "N";
    69. }
    70. //结构体初始化
    71. private void StructInit()
    72. {
    73. boundaries.x_left = 120;
    74. boundaries.x_right = 240;
    75. boundaries.y_up = 80;
    76. boundaries.y_down = 160;
    77. boundaries.d_min = 50;
    78. boundaries.d_max = 150;
    79. }
    80. //获取ini配置文件信息
    81. private void GetIni()
    82. {
    83. ini_RW.FileName = System.Windows.Forms.Application.StartupPath + "\\Config.ini";
    84. this.videoAddress.Text = ini_RW.ReadIni("VideoUrl", "videourl", "");
    85. this.ipAddress.Text = ini_RW.ReadIni("ControlUrl", "controlUrl", "");
    86. this.portBox.Text = ini_RW.ReadIni("ControlPort", "controlPort", "");
    87. }
    88. //修改配置
    89. private void setBtn_Click(object sender, RoutedEventArgs e)
    90. {
    91. ini_RW.WriteIni("VideoUrl", "videourl", this.videoAddress.Text);
    92. ini_RW.WriteIni("ControlUrl", "controlUrl", this.ipAddress.Text);
    93. ini_RW.WriteIni("ControlPort", "controlPort", this.portBox.Text);
    94. System.Windows.MessageBox.Show("配置成功!请重启程序以使配置生效。",
    95. "配置信息", MessageBoxButton.OK,
    96. MessageBoxImage.Information);
    97. //this.Close();
    98. }
    99. //命令发送函数
    100. void SendData(string data)
    101. {
    102. try
    103. {
    104. IPAddress ips = IPAddress.Parse(ipAddress.Text.ToString());//("192.168.8.1");
    105. IPEndPoint ipe = new IPEndPoint(ips, Convert.ToInt32(portBox.Text.ToString()));//把ip和端口转化为IPEndPoint实例
    106. Socket c = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建一个Socket
    107. c.Connect(ipe);//连接到服务器
    108. byte[] bs = Encoding.ASCII.GetBytes(data);
    109. c.Send(bs, bs.Length, 0);//发送测试信息
    110. c.Close();
    111. }
    112. catch (Exception e)
    113. {
    114. System.Windows.Forms.MessageBox.Show(e.Message);
    115. }
    116. }
    117. //跟踪物体位置界限判断
    118. private void LineDetect(int _x, int _y, int _d)
    119. {
    120. try
    121. {
    122. //判断转动方向
    123. if (_x > 0 && _x <= boundaries.x_left)
    124. {
    125. SendData(CMD_TURN_LEFT);
    126. }
    127. else if (x > boundaries.x_right && x < cap_w)
    128. {
    129. SendData(CMD_TURN_RIGHT);
    130. }
    131. //判断是否前进
    132. else if (_d > boundaries.d_min && _d < boundaries.d_max)
    133. {
    134. SendData(CMD_FORWARD);
    135. }
    136. else if ((_x > boundaries.x_left && _x < boundaries.x_right)&&
    137. (_d >= 0 && _d <= boundaries.d_min ))
    138. {
    139. SendData(CMD_LOST);
    140. }
    141. else
    142. {
    143. SendData(CMD_STOP);
    144. }
    145. }
    146. catch { };
    147. }
    148. //物体位置初始化
    149. private void SetPosition()
    150. {
    151. var color = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#FFACAAAA"));
    152. objEllipse.Height = 30;
    153. objEllipse.Width = 30;
    154. objEllipse.Fill = color;
    155. var left_distance = (cap_w - objEllipse.Width) / 2;
    156. var top_distance = (cap_h - objEllipse.Height) / 2;
    157. Canvas.SetLeft(objEllipse, left_distance);
    158. Canvas.SetTop(objEllipse, top_distance);
    159. }
    160. //跟踪物体位置更新函数
    161. private void PositionUpdate(int x, int y, int d)
    162. {
    163. LineDetect(x, y, d);
    164. Canvas.SetLeft(objEllipse, x);
    165. Canvas.SetTop(objEllipse, y);
    166. posLable.Content = x + " , " + y + " , " + d;
    167. }
    168. //线程函数
    169. private void ThreadCapShow()
    170. {
    171. try
    172. {
    173. while (true)
    174. {
    175. this.Dispatcher.Invoke(
    176. new Action(
    177. delegate
    178. {
    179. string ip = this.videoAddress.Text;
    180. Camshift(ip, ref x, ref y, ref d);
    181. PositionUpdate(x - 15, y - 15, d);
    182. }
    183. ));
    184. }
    185. }
    186. catch { };
    187. }
    188. //打开跟踪窗口
    189. private void openBtn_Click(object sender, RoutedEventArgs e)
    190. {
    191. try
    192. {
    193. Thread m_thread = new Thread(ThreadCapShow);
    194. m_thread.IsBackground = true;
    195. m_thread.Start();
    196. }
    197. catch { };
    198. }
    199. }
    200. }

    例程源代码、样机3D文件资料内容详见 双轮云台小车-追踪彩色目标

  • 相关阅读:
    redis的原理和源码-慢查询日志&监视器
    代码随想录算法训练营第23期day2 | 977.有序数组的平方 、209.长度最小的子数组、59.螺旋矩阵II
    4种常见的鉴权方式及说明
    【故障分类】基于注意力机制的卷积神经网络结合双向长短记忆神经网络CNN-BiLSTM-attention实现数据分类附matlab代码
    学习SpringMVC,建立连接,请求,响应 SpringBoot初学,如何前后端交互(后端版)?最简单的能通过网址访问的后端服务器代码举例
    小米/华为怎样找回手机联系人?告别焦虑,2个紧急救援指南
    【学习笔记】minIO分布式文件服务系统
    Namomo Summer Camp 23 Day 1
    2.策略模式
    Ansys(Maxwell、Simplorer)与Simulink联合仿真(二)直线电机
  • 原文地址:https://blog.csdn.net/Robotway/article/details/130728616