|
【R207】舵机云台
作者:机器谱
概述 体感姿态跟随 追踪球形目标 |
2.结构说明
该样机由一个 舵机 加一个 舵机关节模组 构成。
1.运动功能说明
舵机云台下方的舵机可以提供一个左右摆动的动作,同时上方横置的关节模组可以提供一个上下摆动的动作。在这两部分的配合下,云台的执行端端(即:关节模组的U型支架)可以灵活地走出一个近似半球的运动轨迹。
3.运动功能实现
3.1 电子硬件
在这个示例中,我们采用了以下硬件,请大家参考:
3.2 编写程序
编程环境:Arduino 1.8.19
舵机云台的控制关键是驱动舵机和舵机关节模组。
云台运动的代码(点击查看:SimultaneousMovement.ino)
主控板 | Basra(兼容Arduino Uno) |
扩展板 | Bigfish2.1 |
电池 | 7.4V锂电池 |
序号 | 内容 |
1 | 样机3D文件 |
2 | 例程源代码 |
将两个舵机接在扩展板的D3以及D4舵机接口上。
4.扩展样机
使用不同的舵机和支架,可以构建出不同形态的云台,如图所示:
5.资料清单
【整体打包】-【207】舵机云台-概述-资料下载.zip | 918.78KB | 下载61次 | 下载 |
体感姿态跟随
1. 功能说明
本文示例将实现R207样机舵机云台根据 六轴陀螺仪传感器 的数据实现姿态跟随的功能。
2. 电子硬件
在这个示例中,我们采用了以下硬件,请大家参考:
主控板 | |
扩展板 | |
传感器 | 六轴陀螺仪 |
电池 | 7.4V锂电池 |
物料 | 测试数据 |
舵机0 | 0 --- 180 |
舵机1 | 0 --- 110 |
陀螺仪的加速度包X轴数据 | 从左到右转动陀螺仪,数据从(+1.01,-1.05)逐渐减小;中值为0; |
陀螺仪的加速度包Y轴数据 | 沿Y轴俯仰陀螺仪时,数据从(-1.15,1.01)逐渐增大;中值为0; |
/*------------------------------------------------------------------------------------ 版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved. Distributed under MIT license.See file LICENSE for detail or copy at https://opensource.org/licenses/MIT by 机器谱 2023-04-24 https://www.robotway.com/ ------------------------------*/ /* 功能:云台可以根据陀螺仪传感器的数据实现姿态跟随 接线:陀螺仪传感器接在扩展板的Gnd、Vcc(3.3v)、RX、TX; 底座舵机0号接在扩展板的(Gnd、Vcc、D4); 摆动舵机1号接在扩展板的(Gnd、Vcc、D3) */ #include<Servo.h> //调用舵机库函数 #include<Math.h> Servo myservo[2]; //声明两个舵机 int myservopin[2] = {4, 3}; // 定义舵机的引脚 #define Gyroscope_left_LimitAngle_X -1.05 //读取到陀螺仪 X 轴向左偏的极限数值 #define Gyroscope_Right_LimitAngle_X 1.01 //读取到陀螺仪 X 轴向右偏的极限数值 #define Gyroscope_Middle_LimitAngle_X 0 //读取到陀螺仪 X 轴平放时的数值 #define Gyroscope_left_LimitAngle_Y -1.05 //读取到陀螺仪 Y 轴向左偏的极限数值 #define Gyroscope_Right_LimitAngle_Y 1.01 //读取到陀螺仪 Y 轴向右偏的极限数值 #define Gyroscope_Middle_LimitAngle_Y 0 //读取到陀螺仪 Y 轴平放时的数值 #define Servo_One_Mix_Angle 0 //1号舵机最小角度 #define Servo_One_Max_Angle 180 //1号舵机最大角度 #define Servo_Two_Mix_Angle 0 //2号舵机最小角度 #define Servo_Two_Max_Angle 110 //2号舵机最大角度 #define Servo_Speed 10 //舵机速度 unsigned char Re_buf[11],counter=0; unsigned char sign=0; float a[3],w[3],angle[3],T; int servo_angle_current[2] = {0,0}; float value_init[2]={90,90}; float f = 10.0; //舵机的频率 void setup() { Serial.begin(115200); //打开串口,并设置波特率为115200 myservo[0].attach(myservopin[0]); myservo[1].attach(myservopin[1]); } void loop() { Get_gyroscope_And_Control(); //根据陀螺仪传感器的数据实现姿态跟随 } |
电路连接说明:
① 六轴陀螺仪传感器连接在Bigfish扩展板的GND、VCC(3.3V)、RX、TX;
② 底座舵机0号连接在Bigfish扩展板的(GND、VCC、D4);
③ 摆动舵机1号连接在Bigfish扩展板的(GND、VCC、D3)。
3. 功能实现
编程环境:Arduino 1.8.19
实现思路:舵机云台可以根据陀螺仪传感器的数据实现姿态跟随。
六轴陀螺仪实物与空间坐标系的对应关系:
3.1 测试数据
① 测试舵机云台的两个舵机初始角度,记录数据;
② 测试六轴陀螺仪传感器的姿态数据。
下表是本实验中测试出的实验数据,大家可参考格式,测试出自己的实验数据。
3.2 示例程序
根据已测数据编写程序,下面提供一个参考例程(body_feeling_attitude.ino):
注意:本次实验测试数据对应的程序位置如下图所示,大家可尝试输入自己的实验数据参数,修改这段程序,使舵机云台的姿态跟随更加流畅。
4. 资料清单
序号 | 内容 |
1 | 【R207】-体感姿态跟随-程序源代码 |
【整体打包】-【R207】舵机云台-体感姿态跟随-资料附件.zip | 4.93KB | 下载14次 | 下载 |
追踪球形目标
1. 功能说明
在R207样机舵机云台上安装一个摄像头,本文示例将实现舵机云台追踪球形物体的功能。
2. 电子硬件
在这个示例中,我们采用了以下硬件,请大家参考:
主控板 | |
扩展板 | |
电池 | 7.4V锂电池 |
通信 | |
其它 | 摄像头 |
/*------------------------------------------------------------------------------------ 版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved. Distributed under MIT license.See file LICENSE for detail or copy at https://opensource.org/licenses/MIT by 机器谱 2023-04-24 https://www.robotway.com/ ------------------------------*/ #include <Servo.h>
#define BOTTOM_SERVO_MIN 10 #define BOTTOM_SERVO_MAX 175 #define TOP_SERVO_MIN 85 #define TOP_SERVO_MAX 175
const String CMD_UP = "U"; const String CMD_DOWN = "D"; const String CMD_LEFT = "L"; const String CMD_RIGHT = "R";
Servo myServo[2]; int servo_value[2] = {85, 105}; int port0 = 4; int port1 = 7; int servo_move_angle = 1;
void setup() { Serial.begin(9600); ServoInit(); delay(1000); }
void loop() { String s = SerialRead();
if(s != "") { if(s == CMD_UP) ServoAdjust(port1, -servo_move_angle); else if(s == CMD_DOWN) ServoAdjust(port1, servo_move_angle); else if(s == CMD_LEFT) ServoAdjust(port0, -servo_move_angle); else if(s == CMD_RIGHT) ServoAdjust(port0, servo_move_angle); } }
String SerialRead() { String str = ""; while(Serial.available()) { str += char(Serial.read()); } return str; }
void ServoInit() { myServo[0].attach(port0); myServo[1].attach(port1);
for(int i=0;i<2;i++) { myServo[i].write(servo_value[i]); } }
void ServoAdjust(int which, int where) { switch(which) { case 4: servo_value[0] += where;
if(servo_value[0] <= BOTTOM_SERVO_MIN) servo_value[0] = BOTTOM_SERVO_MIN; else if(servo_value[0] >= BOTTOM_SERVO_MAX) servo_value[0] = BOTTOM_SERVO_MAX;
myServo[0].write(servo_value[0]); break; case 7: servo_value[1] += where;
if(servo_value[1] <= TOP_SERVO_MIN) servo_value[1] = TOP_SERVO_MIN; else if(servo_value[1] >= TOP_SERVO_MAX) servo_value[1] = TOP_SERVO_MAX;
myServo[1].write(servo_value[1]); break; default: break; } } |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Forms; using System.Runtime.InteropServices; using System.Threading; using System.Net; using System.Net.Sockets; namespace Project { /// <summary> /// 形状跟踪,球 /// </summary> public partial class MainWindow : Window { //导入 HoughCircles.dll 动态链接库 [DllImport("HoughCircles_DLL")] public static extern void HoughCircles([MarshalAs(UnmanagedType.LPStr)]string ip_address, //视频地址 ref int xpos, //跟踪物体中心X坐标 ref int ypos); //跟踪物体中心Y坐标 //定义窗口大小 int cap_w = 320, cap_h = 240; //跟踪物体中心 x, y 坐标值,物体轮廓半径r int x = 0, y = 0; //定义命令变量 string CMD_UP = "", CMD_DOWN = "", CMD_TURN_LEFT = "", CMD_TURN_RIGHT = ""; //结构体 public struct Boundaries { public int x_left; public int x_right; public int y_up; public int y_down; } Boundaries boundaries = new Boundaries(); public MainWindow() { InitializeComponent(); } private void Window_Loaded(object sender, RoutedEventArgs e) { GetIni(); SetPosition(); CmdInit(); StructInit(); } //变量初始化 private void CmdInit() { CMD_UP = "U"; CMD_DOWN = "D"; CMD_TURN_LEFT = "L"; CMD_TURN_RIGHT = "R"; } //结构体初始化 private void StructInit() { boundaries.x_left = 100; boundaries.x_right = 200; boundaries.y_up = 80; boundaries.y_down = 160; } //获取ini配置文件信息 private void GetIni() { ini_RW.FileName = System.Windows.Forms.Application.StartupPath + "\\Config.ini"; this.videoAddress.Text = ini_RW.ReadIni("VideoUrl", "videourl", ""); this.ipAddress.Text = ini_RW.ReadIni("ControlUrl", "controlUrl", ""); this.portBox.Text = ini_RW.ReadIni("ControlPort", "controlPort", ""); } //修改配置 private void setBtn_Click(object sender, RoutedEventArgs e) { ini_RW.WriteIni("VideoUrl", "videourl", this.videoAddress.Text); ini_RW.WriteIni("ControlUrl", "controlUrl", this.ipAddress.Text); ini_RW.WriteIni("ControlPort", "controlPort", this.portBox.Text); System.Windows.MessageBox.Show("配置成功!请重启程序以使配置生效。", "配置信息", MessageBoxButton.OK, MessageBoxImage.Information); //this.Close(); } //命令发送函数 void SendData(string data) { try { IPAddress ips = IPAddress.Parse(ipAddress.Text.ToString());//("192.168.8.1"); IPEndPoint ipe = new IPEndPoint(ips, Convert.ToInt32(portBox.Text.ToString()));//把ip和端口转化为IPEndPoint实例 Socket c = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建一个Socket c.Connect(ipe);//连接到服务器 byte[] bs = Encoding.ASCII.GetBytes(data); c.Send(bs, bs.Length, 0);//发送测试信息 c.Close(); } catch (Exception e) { System.Windows.Forms.MessageBox.Show(e.Message); } } //跟踪物体位置界限判断 private void LineDetect(int _x, int _y) { if (_x > 0 && _x <= boundaries.x_left) { SendData(CMD_TURN_LEFT); } else if (x > boundaries.x_right && x < cap_w) { SendData(CMD_TURN_RIGHT); } else if (_y > 0 && _y <= boundaries.y_up) { SendData(CMD_DOWN); } else if (_y > boundaries.y_down && _y < cap_h) { SendData(CMD_UP); } } //物体位置初始化#FFACAAAA private void SetPosition() { var color = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#00008B")); objEllipse.Height = 30; objEllipse.Width = 30; objEllipse.Fill = color; var left_distance = (cap_w - objEllipse.Width) / 2; var top_distance = (cap_h - objEllipse.Height) / 2; Canvas.SetLeft(objEllipse, left_distance); Canvas.SetTop(objEllipse, top_distance); } //跟踪物体位置更新函数 private void PositionUpdate(int x, int y) { LineDetect(x, y); Canvas.SetLeft(objEllipse, x); Canvas.SetTop(objEllipse, y); posLable.Content = x + " , " + y; } //线程函数 private void ThreadCapShow() { try { while (true) { this.Dispatcher.Invoke( new Action( delegate { string ip = this.videoAddress.Text; HoughCircles(ip, ref x, ref y); PositionUpdate(x - 15, y - 15); } )); } } catch { }; } //打开跟踪窗口 private void openBtn_Click(object sender, RoutedEventArgs e) { try { Thread m_thread = new Thread(ThreadCapShow); m_thread.IsBackground = true; m_thread.Start(); } catch { }; } } } |
电路连接说明:
① 将2510通信转接板连接到Bigfish扩展板的扩展坞上面;
② 用3根母对母杜邦线将2510通信转接板与WiFi路由器连接起来:GND-GND、RX-RX、TX-TX;
③ 找到1根USB线,一端连接到2510通信转接板接口上,另一端连接到WiFi路由器USB接口上;
④ 将摄像头线连接到WiFi路由器接口上。
3. 功能实现
实现思路:实现舵机云台追踪蓝色小球。
3.1 工作原理
① 摄像头采集图像信息;
② 通过WiFi将图像信息传递给PC端(VS2015配置的OpenCV环境);
③ 在PC端使用OpenCV对图像转化为灰度图像;
④ 检测圆形,并且计算出圆中心坐标;
⑤ 采用九宫格方式对摄像显示图像进行分割;
⑥ 确定目标物体在显示图像的所处九宫格位置;
⑦ 如果目标图像超出九宫格位置的中心,调整摄像头矫正偏移使目标物体在屏幕中心位置;
⑧ 调整摄像头需要上位机通过WiFi给下位机发送矫正指令,下位机需要接收信号,并且让安装了摄像头的舵机云台做出相应的矫正动作。
3.2 示例程序
编程环境:Arduino 1.8.19
① 下位机例程
将参考例程(example.ino)下载到主控板,开启路由器,将路由器与主控板TX、Rx串口进行连接,同时将PC连接至路由器WIFI网络。下位机接收上位机处理的图像信息结果控制舵机云台相应运动,云台跟随目标物体运动。
② 上位机例程
下面提供一个可以实现舵机云台追踪球形物体的参考例程(MainWindow.xaml.cs),大家可参考演示视频完成该实验。
4. 资料清单
序号 | 内容 |
1 | 【R207】-追踪球形目标-例程源代码 |
2 | 【R207】-追踪球形目标-样机3D文件 |
【整体打包】-【R207】舵机云台-追踪球形目标-资料附件.zip | 42.86MB | 下载3次 | 下载 |