基于Pymavlink协议的BlueROV开发

1 BlueROV概述

1.1 什么是ROV

维基百科
遥控潜水器(Remotely operated underwater vehicle,缩写ROV)是一个无人的水下航行器,以电缆连接到母船的人员操作。常搭载水下光源和照相机、摄影机、机械手臂、声纳等。因为具有机械手臂,所以又称为水下机器人。
百度百科
ROV,即遥控无人潜水器,是用于水下观察、检查和施工的水下机器人。

1.2 什么是BlueROV

国外一家公司开发的ROV产品,产品名字叫做BlueROV。

2 BlueROV结构介绍(以中型为例)

2.1 几个视图

下面先给出BlueROV的几个视图:
正视图侧视图

2.2 机械结构

如图,我大致将BlueROV机械结构分为了机架、电池仓、主控仓和其他功能模块四部分,接下来会对这几部分进行逐一介绍。

2.2.1 机架

Bluerov除去电池仓、主控仓和其他比如螺旋桨和灯等功能模块以外都是机架,机架机架主要起固定电池仓、主控仓和其他功能模块的作用。另外机架上面还可以固定后加的铅块、浮力块等物品用来调节使机体平衡。机架上部固定有浮力较大的浮体,为机体提供充足的浮力,保证机体在自然状态下或者电源耗尽的状态下能够自然浮出水面。
另外机架上面还预留了很多小孔,为开发者预留了很多接口,方便后续安装机械爪、声纳等拓展件。
机架

2.2.2 电池仓

Bluerov机架上共固定有两个仓,其中下面的仓为电池仓。里面放置了一块18.0Ah的锂电池,从电池仓里面引出来一条线为控制仓供电。
BlueROV电池

2.2.3 主控仓

BlueROV机架上方固定的仓是主控仓。里面主要放置电源、电调、树莓派、Pixhawk、摄像头、载波模块等元器件,从控制仓里引出电源线和信号线用来为螺旋桨和灯提供能源和信号。

2.3 电气部件

2.3.1 连线

其中控制仓的接线图如下图:
控制仓接线图
电脑通过载波模块与树莓派相连;树莓派上通过USB-MicroUSB线与pixhawk相连,另外树莓派上还连接有5V6A电源为树莓派供电,一个摄像头负责获取图像;pixhawk上连接有相机云台、防水灯、电调-螺旋桨、5V6A电源、功率计、压强计。
下面对这些元器件进行逐一介绍。

2.3.2 主控仓元器件

1、树莓派
使用价格低廉的树莓派3B,用来控制Pixhawk,连接摄像头并传回图像信息。
树莓派3B
2、电源
为树莓派3B和Pixhawk供电。
电源
3、相机云台
用来控制相机上下运动,切换视角。
相机云台
4、相机
感知外界环境,传回图像信息。
相机
5、防水灯
照亮漆黑的海底(水底)环境,方便下水作业。
防水灯
6、电调
电调有“电流控制”作用,电调内部电路有一套MOSFET管(“功率管”)。电流输入电调,内部电路接收来自接收机的信号,根据信号,把电流作出合适的“控制”,然后把“控制”后的电流输出到马达,从而控制电机的启停及转速。至于控制部分,控制信号控制电调在单位时间内开关的次数。针对电机不同,可分为有刷电调和无刷电调;有刷电调就是简单的直流输出;无刷电调就是把直流电转换成为3相交流电。电调输入是直流,可以接稳压电源,或者锂电池。
电调
7、螺旋桨
推动ROV完成一系列运动,可以正转和反转,取决于输入的pwm控制信号。
螺旋桨
8、功率计
及时检测电压电流等供电情况。
功率计
9、压强计
及时检测ROV所处的位置的水压,从而计算出当时的深度,使ROV能稳定在某一深度下。
压强计
10、Pixhawk
具有8个主输出通道和6个辅助输出通道,另外还有一个SB和一个RC输入通道。具有以下特征:
● 能够加载任何ArduPilot二进制固件文件(直升机,飞机,漫游者/船,潜艇)
● 包含用于连接多个外设的输入和输出连接
● 包含嵌入式 IMU、磁罗盘和陀螺仪,用于确定车辆的方向
● 能够保存车辆日志
Pixhawk

3 软件控制

下面内容主要参考链接ardusub,部分内容来自链接文档,部分是个人经验总结。下方内容目的在于帮助初学者快速上手BlueROV的使用。

3.1 手柄控制

手柄控制需要搭配相应的控制软件QGRoundContral(下载链接:QGC下载),简称QGC,支持Windows,Ubuntu Linux等多种平台。Ubuntu安装QGC与Windows略有不同,安装方法详见QGroundControl Installation。软件安装以后需要手动配置以太网IP(192.168.2.1)才能实现连接,正常连接时左上角会出现“Ready To Fly”字样。其中部分电脑机型能正常连接但是不出现视频画面属正常情况,目前尚未找到解决方法。
软件安装好以后需要QGC连接ROV和手柄,打开“Vehicle Setup View”中的“Joystick”勾选“Enable joystick input”后,手柄此方能正常使用,手柄键位可自行摸索或者参考ardusub中QGroundControl一栏。
QGC

3.2 代码控制

3.2.1 环境配置

其本质是装一个pip包,详见链接Installation,这里仍然有几点需要说明。
Windows:
其中Windows下需要提前安装好Python3解释器,这里我推荐3.8左右版本的(高版本的似乎不支持mavproxy包,本人亲测,结果pip报错),Python3安装方法自行百度,
Ubuntu:
此时默认你已经安装好了ubuntu系统(18.04和20.04都可),因为这两个版本的ubuntu系统自带python3解释器,因此按照上述链接Installation教程一步步安装即可。

3.2.2 Pymavlink协议解读

Bluerov使用了开源的Pymavlink协议,Pymavlink协议是对Mavlink协议的Python封装,接下来是一些代码并对其进行介绍(注意标*的是重点)。
如果在学习过程中遇到问题可以访问生产BlueROV的bluerobotics公司搭建的论坛链接(友情提示:提问问题时用英文,尽量别出现中文字样,国外反华势力不容小觑),也欢迎大家访问我的个人主页交流讨论:robotmark。
1、Pixhawk直接与Linux系统电脑相连

# pixhawk通过串行连接到计算机(linux系统计算机)
from pymavlink import mavutil
master = mavutil.mavlink_connection("/dev/ttyACM0", baud=115200)    # 第一个参数是pix在linux操作系统中对应的文件名,第二个参数对应的是端口号
master.reboot_autopilot()

*2、在自己电脑上运行代码通过树莓派连接到pix

import time
from pymavlink import mavutilmaster = mavutil.mavlink_connection('udpin:0.0.0.0:14550')
master.wait_heartbeat()while True:try:print(master.recv_match().to_dict())except:passtime.sleep(0.1)

如果出现以下信息就证明连接成功:

{'mavpackettype': 'AHRS2', 'roll': -0.11364290863275528, 'pitch': -0.02841472253203392, 'yaw': 2.0993032455444336, 'altitude': 0.0, 'lat': 0, 'lng': 0}
{'mavpackettype': 'AHRS3', 'roll': 0.025831475853919983, 'pitch': 0.006112074479460716, 'yaw': 2.1514968872070312, 'altitude': 0.0, 'lat': 0, 'lng': 0, 'v1': 0.0, 'v2': 0.0, 'v3': 0.0, 'v4': 0.0}
{'mavpackettype': 'VFR_HUD', 'airspeed': 0.0, 'groundspeed': 0.0, 'heading': 123, 'throttle': 0, 'alt': 3.129999876022339, 'climb': 3.2699999809265137}
{'mavpackettype': 'AHRS', 'omegaIx': 0.0014122836291790009, 'omegaIy': -0.022567369043827057, 'omegaIz': 0.02394154854118824, 'accel_weight': 0.0, 'renorm_val': 0.0, 'error_rp': 0.08894175291061401, 'error_yaw': 0.0990816056728363}

3、在树莓派上连接pix

import time
from pymavlink import mavutildef wait_conn():msg = Nonewhile not msg:master.mav.ping_send(int(time.time() * 1e6),0,0,0)msg = master.recv_match()time.sleep(0.5)master = mavutil.mavlink_connection('udpout:0.0.0.0:9000')
wait_conn()while True:try:print(master.recv_match().to_dict())except:passtime.sleep(0.1)

如果终端出现了以下输出,证明连接成功:

{'mavpackettype': 'AHRS2', 'roll': -0.11364290863275528, 'pitch': -0.02841472253203392, 'yaw': 2.0993032455444336, 'altitude': 0.0, 'lat': 0, 'lng': 0}
{'mavpackettype': 'AHRS3', 'roll': 0.025831475853919983, 'pitch': 0.006112074479460716, 'yaw': 2.1514968872070312, 'altitude': 0.0, 'lat': 0, 'lng': 0, 'v1': 0.0, 'v2': 0.0, 'v3': 0.0, 'v4': 0.0}
{'mavpackettype': 'VFR_HUD', 'airspeed': 0.0, 'groundspeed': 0.0, 'heading': 123, 'throttle': 0, 'alt': 3.129999876022339, 'climb': 3.2699999809265137}
{'mavpackettype': 'AHRS', 'omegaIx': 0.0014122836291790009, 'omegaIy': -0.022567369043827057, 'omegaIz': 0.02394154854118824, 'accel_weight': 0.0, 'renorm_val': 0.0, 'error_rp': 0.08894175291061401, 'error_yaw': 0.0990816056728363}

*4、锁上/解锁ROV

"""
用pymavlink协议去解锁和锁上飞行器
"""# 解锁
from pymavlink import mavutil
def arm():master = mavutil.mavlink_connection("/dev/ttyACM0", baud=115200)master.wait_heartbeat()master.mav.command_long_send(master.target_system,master.target_component,mavutil.mavlink.MAV_CMD_COMPONENT_ARM_DISARM,0,1, 0, 0, 0, 0, 0, 0)print("Waiting for the vehicle to arm")print('Armed!')# 上锁
def disarm():master = mavutil.mavlink_connection('udpin:0.0.0.0:14550')master.wait_heartbeat()master.mav.command_long_send(master.target_system,master.target_component,mavutil.mavlink.MAV_CMD_COMPONENT_ARM_DISARM,0,0, 0, 0, 0, 0, 0, 0)master.motors_disarmed_wait()

*5、更改飞行模式

import sys
from pymavlink import mavutil
import timemaster = mavutil.mavlink_connection("/dev/ttyACM0", baud=115200)
master.wait_heartbeat()# try ['STABILIZE', 'ACRO', 'ALT_HOLD', 'AUTO', 'GUIDED', 'CIRCLE', 'SURFACE', 'POSHOLD', 'MANUAL']def change_mode(mode):# Check if mode is availableif mode not in master.mode_mapping():print('Unknown mode : {}'.format(mode))print('Try:', list(master.mode_mapping().keys()))sys.exit(1)# Get mode IDmode_id = master.mode_mapping()[mode]master.mav.set_mode_send(master.target_system,mavutil.mavlink.MAV_MODE_FLAG_CUSTOM_MODE_ENABLED,mode_id)

*6、发送操作杆控制

import os
from pymavlink import mavutilimport time
master = mavutil.mavlink_connection("/dev/ttyACM0", baud=115200)
master.wait_heartbeat()
def set_rc_channel_pwm(channel_id, pwm=1500):if channel_id < 1 or channel_id > 18:print("Channel does not exist.")returnrc_channel_values = [65535 for _ in range(18)]rc_channel_values[channel_id - 1] = pwmmaster.mav.rc_channels_override_send(master.target_system,                # target_systemmaster.target_component,             # target_component*rc_channel_values)                  # RC channel list, in microseconds.def static():set_rc_channel_pwm(1,1500)  # 前后俯仰,中值1500set_rc_channel_pwm(2,1500)  # 左右翻滚,中值1500set_rc_channel_pwm(3,1500)  # 上浮下潜,中值1500set_rc_channel_pwm(4,1500)  # 左右偏航,中值1500set_rc_channel_pwm(5,1500)  # 前进后退,中值1500set_rc_channel_pwm(6,1500)  # 左右横移,中值1500set_rc_channel_pwm(8,1100)  # 左灯,中值1100set_rc_channel_pwm(9,1100)  # 右灯,中值1100def aim(pwm1,pwm2,pwm3,pwm4):set_rc_channel_pwm(3,pwm1)#上浮下潜set_rc_channel_pwm(4,pwm2)#左拐右拐set_rc_channel_pwm(5,pwm3)#前进后退set_rc_channel_pwm(6,pwm4)#左右平移set_rc_channel_pwm(1,1500)set_rc_channel_pwm(2,1500)set_rc_channel_pwm(9, 1100)set_rc_channel_pwm(10, 1100)

7、发送手动控制

from pymavlink import mavutilmaster = mavutil.mavlink_connection('udpin:0.0.0.0:14550')
master.wait_heartbeat()master.mav.manual_control_send(master.target_system,500,-500,250,500,0)buttons = 1 + 1 << 3 + 1 << 7
master.mav.manual_control_send(master.target_system,0,0,500,0,
buttons)

*8、读取所有参数

"""读出ROV所有参数并写入到param_data.log文件中"""
import time
import sysfrom pymavlink import mavutilmaster = mavutil.mavlink_connection('udpin:0.0.0.0:14550')
master.wait_heartbeat()master.mav.param_request_list_send(master.target_system, master.target_component
)
file=open('param_data.log',mode='a',encoding="utf-8")
file.write('日志记录的时间: ')
file.write(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
file.write('\n')
while True:#time.sleep(0.01)try:message = master.recv_match(type='PARAM_VALUE', blocking=True).to_dict()print('name: %s\tvalue: %d' % (message['param_id'],message['param_value']))file.write('name: %s\tvalue: %d' % (message['param_id'],message['param_value']))file.write('\n')except Exception as error:print(error)sys.exit(0)

9、读取和写入参数

import time
from pymavlink import mavutilmaster = mavutil.mavlink_connection('udpin:0.0.0.0:14550')
master.wait_heartbeat()master.mav.param_request_read_send(master.target_system, master.target_component,b'SURFACE_DEPTH',-1
)message = master.recv_match(type='PARAM_VALUE', blocking=True).to_dict()
print('name: %s\tvalue: %d' %(message['param_id'].decode("utf-8"), message['param_value']))time.sleep(1)master.mav.param_set_send(master.target_system, master.target_component,b'SURFACE_DEPTH',-12,mavutil.mavlink.MAV_PARAM_TYPE_REAL32
)message = master.recv_match(type='PARAM_VALUE', blocking=True).to_dict()
print('name: %s\tvalue: %d' %(message['param_id'].decode("utf-8"), message['param_value']))time.sleep(1)master.mav.param_request_read_send(master.target_system, master.target_component,b'SURFACE_DEPTH',-1
)message = master.recv_match(type='PARAM_VALUE', blocking=True).to_dict()
print('name: %s\tvalue: %d' %(message['param_id'].decode("utf-8"), message['param_value']))

*10、对同一类型的消息进行过滤

from telnetlib import DO
from pymavlink import mavutil
import matplotlib.pyplot as plt
import timedef DONotFilter():"""不对信息进行过滤的函数"""master = mavutil.mavlink_connection('udpin:0.0.0.0:14550')while True:msg = master.recv_match()if not msg:continueif msg: #会打印出所有msg# print("\n\n*****Got message: %s*****" % msg.get_type())print("Message: %s" % msg)def DOFilter_From_SingleType(msg_type):    # msg_type是一个字符串类型的数据"""从单种类型的信息进行过滤"""master = mavutil.mavlink_connection('udpin:0.0.0.0:14550')x_time=[]y_xacc=[]y_yacc=[]y_zacc=[]plt.ion()while True:msg = master.recv_match()#print(msg)if not msg:continueif msg.get_type() == msg_type:#print("\n\n*****Got message: %s*****" % msg.get_type()) #会打印出消息的类型#print("Message: %s" % msg)  #会打印出进行上面一层过滤以后的所有msgprint(msg)x_time.append(time.time())y_xacc.append(msg.xacc)y_yacc.append(msg.yacc)y_zacc.append(msg.zacc)plt.clf()              # 清除之前画的图plt.plot(x_time,y_xacc)        # 画出当前 ax 列表和 ay 列表中的值的图形plt.plot(x_time,y_yacc)plt.plot(x_time,y_zacc)plt.xlabel('time')plt.ylabel('IMU')plt.pause(0.00000001)         # 暂停一秒plt.ioff()             # 关闭画图的窗口if __name__=='__main__':DOFilter_From_SingleType('RAW_IMU')# DO_NotFilter()

*11、对不同类型的消息进行过滤

"""用来接收不同类型的信息"""
from pymavlink import mavutil
import matplotlib.pyplot as pltmessage_types = {'VFR_HUD', 'RAW_IMU', 'ATTITUDE', 'VIBRATION'}  # 把要获取消息的类型添加到字典中def handle_VFRHUD_message(msg):"""处理VFR_HUD类型的消息"""global VFRHUD_ParamsListgroundspeed, heading, alt = msg.groundspeed, msg.heading, msg.altVFRHUD_ParamsList=[groundspeed, heading, alt]# print('VFR_HUD', groundspeed, heading, alt)return VFRHUD_ParamsListdef handle_RAWIMU_message(msg):"""处理RAW_IMU类型的消息"""global RAWIMU_ParamsListxacc, yacc, zacc = msg.xacc, msg.yacc, msg.zacc# print('RAW_IMU', xacc, yacc, zacc)RAWIMU_ParamsList=[xacc, yacc, zacc]return RAWIMU_ParamsListdef handle_ATTITUDE_message(msg):"""处理ATTITUDE类型的消息"""global ATTITUDE_ParamsListroll, pitch, yaw, rollspeed, pitchspeed, yawspeed = msg.roll, msg.pitch, msg.yaw, msg.rollspeed, msg.pitchspeed, msg.yawspeed# print('ATTITUDE', roll, pitch, yaw, rollspeed, pitchspeed, yawspeed)ATTITUDE_ParamsList=[roll, pitch, yaw, rollspeed, pitchspeed, yawspeed]return ATTITUDE_ParamsListdef handle_VIBRATION_message(msg):"""处理VIBRATION类型的消息"""global VIBRATION_ParamsListvibration_x, vibration_y, vibration_z = msg.vibration_x, msg.vibration_y, msg.vibration_z# print('VIBRATION', vibration_x, vibration_y, vibration_z)VIBRATION_ParamsList=[vibration_x, vibration_y, vibration_z]return VIBRATION_ParamsListmessage_handlers = {'VFR_HUD': handle_VFRHUD_message,'RAW_IMU': handle_RAWIMU_message,'ATTITUDE':handle_ATTITUDE_message,'VIBRATION':handle_VIBRATION_message
}# def handle_message(message):
#     type_ = message.get_type()
#     if type_ in message_handlers:
#         message_handlers[type_](message)
#     else:
#         print(type_)# def DOFilter_From_DifferentType():
#     """主函数"""
#     # master = mavutil.mavlink_connection('udpin:0.0.0.0:14550')
#     global master
#     message=master.recv_match(type=message_types, blocking=True)
#     type_=message.get_type()
#     if not message:
#         pass
#     if type_ in message_handlers:
#         # message_handlers[type_](message)
#         if type_=='VFR_HUD':
#             print(message_handlers[type_](message))
#             VFRHUD_ParamsList=message_handlers[type_](message)
#         if type_=='RAW_IMU':
#             print(message_handlers[type_](message))
#             RAWIMU_ParamsList=message_handlers[type_](message)
#         if type_=='ATTITUDE':
#             print(message_handlers[type_](message))
#             ATTITUDE_ParamsList=message_handlers[type_](message)
#         if type_=='VIBRATION':
#             print(message_handlers[type_](message))
#             VIBRATION_ParamsList=message_handlers[type_](message)def DOFilter_From_DifferentType():"""对不同类型的信息进行过滤"""# master = mavutil.mavlink_connection('udpin:0.0.0.0:14550')# message_handlers[type_](message)global type_, msg, VFRHUD_ParamsList, RAWIMU_ParamsList, ATTITUDE_ParamsList, VIBRATION_ParamsListif type_=='VFR_HUD':# print(message_handlers[type_](msg))VFRHUD_ParamsList=message_handlers[type_](msg)if type_=='RAW_IMU':# print(message_handlers[type_](msg))RAWIMU_ParamsList=message_handlers[type_](msg)if type_=='ATTITUDE':# print(message_handlers[type_](msg))ATTITUDE_ParamsList=message_handlers[type_](msg)if type_=='VIBRATION':# print(message_handlers[type_](msg))VIBRATION_ParamsList=message_handlers[type_](msg)AllROV_ParamsList=[VFRHUD_ParamsList,RAWIMU_ParamsList,ATTITUDE_ParamsList,VIBRATION_ParamsList]print(AllROV_ParamsList)if __name__=='__main__':print(11111111111)master = mavutil.mavlink_connection('udpin:0.0.0.0:14550')while 1:msg=master.recv_match(type=message_types, blocking=True)type_=msg.get_type()if type_ in message_handlers:DOFilter_From_DifferentType()else:continue

12、设置请求消息间隔

import time
from pymavlink import mavutilmaster = mavutil.mavlink_connection('udpin:0.0.0.0:14550')
master.wait_heartbeat()def request_message_interval(message_id: int, frequency_hz: float):master.mav.command_long_send(master.target_system, master.target_component,mavutil.mavlink.MAV_CMD_SET_MESSAGE_INTERVAL, 0,message_id,1e6 / frequency_hz,0, 0, 0, 0,0,)request_message_interval(mavutil.mavlink.MAVLINK_MSG_ID_AHRS2, 1)
request_message_interval(mavutil.mavlink.MAVLINK_MSG_ID_ATTITUDE, 2)while True:try:print(master.recv_match().to_dict())except:passtime.sleep(0.1)

*13、控制相机云台转动至一定的角度

import time
from pymavlink import mavutilmaster = mavutil.mavlink_connection('udpin:0.0.0.0:14550')
master.wait_heartbeat()def look_at(tilt, roll=0, pan=0):master.mav.command_long_send(master.target_system,master.target_component,mavutil.mavlink.MAV_CMD_DO_MOUNT_CONTROL,1,tilt,roll,pan,0, 0, 0,mavutil.mavlink.MAV_MOUNT_MODE_MAVLINK_TARGETING)look_at(0,0,0) 	# 归中

*14、如果bluerov外加了机械爪,可以通过pix去直接控制机械爪的舵机

from pymavlink import mavutil
import timedef set_servo_pwm(servo_n, microseconds):master.mav.command_long_send(master.target_system, master.target_component,mavutil.mavlink.MAV_CMD_DO_SET_SERVO,0,servo_n + 8,microseconds,0,0,0,0,0)master = mavutil.mavlink_connection("/dev/ttyACM0", baud=115200)
master.wait_heartbeat()def Ongripper():for us in range(1775, 1925, 25):set_servo_pwm(4, us)time.sleep(0.125)def Offgripper():for us in range(1900, 1600, -25):set_servo_pwm(4, us)time.sleep(0.125)if __name__ == "__main__":Offgripper()

15、高级舵机控制

from pymavlink import mavutilclass RawServoOutput:CMD_SET = mavutil.mavlink.MAV_CMD_DO_SET_SERVOdef __init__(self, master, instance, pwm_limits=(1100, 1500, 1900),set_default=True):self.master     = masterself.instance   = instanceself.min_us, self._current, self.max_us = pwm_limitsself._diff      = self.max_us - self.min_usif set_default:self.set_direct(self._current)def set_direct(self, us):assert self.min_us <= us <= self.max_us, "Invalid input value."self.master.mav.command_long_send(self.master.target_system, self.master.target_component,self.CMD_SET,0,self.instance, us,0,0,0,0,0)self._current = usdef set_ratio(self, proportion):self.set_direct(proportion * self._diff + self.min_us)def inc(self, us=50):self.set_direct(max(self.max_us, self._current+us))def dec(self, us=50):self.set_direct(min(self.min_us, self._current-us))def set_min(self):self.set_ratio(0)def set_max(self):self.set_ratio(1)def center(self):self.set_ratio(0.5)class AuxServoOutput(RawServoOutput):def __init__(self, master, servo_n, main_outputs=8, **kwargs):super().__init__(master, servo_n+main_outputs, **kwargs)self._main_outputs = main_outputs@propertydef servo_n(self):return self.instance - self._main_outputsclass Gripper(AuxServoOutput):def __init__(self, master, servo_n, pwm_limits=(1100, 1100, 1500),**kwargs):super().__init__(master, servo_n, pwm_limits=pwm_limits, **kwargs)def open(self):self.set_max()def close(self):self.set_min()if __name__ == '__main__':from time import sleepautopilot = mavutil.mavlink_connection('udpin:0.0.0.0:14550')autopilot.wait_heartbeat()gripper = Gripper(autopilot, 1)for _ in range(3):gripper.open()sleep(2)gripper.close()sleep(1)

*16、设置目标深度和姿态(只在深度保持下才能用)

import time
import math
from pymavlink import mavutil
from pymavlink.quaternion import QuaternionBasedef set_target_attitude(roll, pitch, yaw):master.mav.set_attitude_target_send(int(1e3 * (time.time() - boot_time)),master.target_system, master.target_component,mavutil.mavlink.ATTITUDE_TARGET_TYPEMASK_THROTTLE_IGNORE,QuaternionBase([math.radians(angle) for angle in (roll, pitch, yaw)]),0, 0, 0, 0)master = mavutil.mavlink_connection("/dev/ttyACM0", baud=115200)
boot_time = time.time()
master.wait_heartbeat()master.arducopter_arm()
master.motors_armed_wait()DEPTH_HOLD = 'ALT_HOLD'
DEPTH_HOLD_MODE = master.mode_mapping()[DEPTH_HOLD]
while not master.wait_heartbeat().custom_mode == DEPTH_HOLD_MODE:master.set_mode(DEPTH_HOLD)roll_angle = pitch_angle = 0
for yaw_angle in range(0, 500, 10):set_target_attitude(roll_angle, pitch_angle, yaw_angle)time.sleep(1)for yaw_angle in range(500, 0, -30):set_target_attitude(roll_angle, pitch_angle, yaw_angle)time.sleep(1)master.arducopter_disarm()
master.motors_disarmed_wait()

另外也可以通过自己读出深度计的数据直接设置目标深度:

"""给ROV设置深度"""
import sys
import time
import matplotlib.pyplot as plt
from pymavlink import mavutil
from move.action import *
from pid_catalog.read import *
from move.arm_disarm import arm,disarm
from move.init import *class UD_PID():def __init__(self, dt, max, min, Kp, Kd, Ki):self.dt = dt    # 循环时长self.max = max  # 操作变量最大值self.min = min  # 操作变量最小值self.Kp = Kp         # 比例增益self.Kd = Kd         # 积分增益self.Ki = Ki         # 微分增益self.integral = 0    # 直到上一次的误差值self.pre_error = 0   # 上一次的误差值def calculate(self, setPoint, pv):# 其中 pv:process value 即过程值,error = setPoint  -   pv      # 误差Pout = self.Kp * error          # 比例项self.integral += error * self.dtIout = self.Ki * self.integral  # 积分项derivative = (error - self.pre_error)/self.dtDout = self.Kd * derivative     # 微分项output =1454+ Pout + Iout + Dout     # 新的目标值if(output > self.max):output = self.maxelif(output < self.min):output = self.minself.pre_error = error         # 保存本次误差,以供下次计算return output
ud_pid = UD_PID(0.1, 1494, 1414, 250, 100 , 100) x_pwm=[]
y_depth=[]# 传入预设的深度和布尔值(相当于一个开关的作用),返回当前的深度
def set_target_depth(depth,Boolean):master = mavutil.mavlink_connection('udpin:0.0.0.0:14550')plt.ion()while Boolean:msg = master.recv_match()if not msg:continueif msg.get_type() == 'VFR_HUD':print(msg.alt)a = ud_pid.calculate(depth,msg.alt)b = int(a)x_pwm.append(b)y_depth.append(msg.alt)plt.clf()plt.plot(x_pwm,y_depth)plt.pause(0.1)plt.ioff()print('b: ',b)throttle(b)return msg.altif __name__=='__main__':    master = mavutil.mavlink_connection('udpin:0.0.0.0:14550')master.wait_heartbeat()disarm()init()arm() #解锁       while 1:set_target_depth(-0.25,True)

通过指南针数据设置目标朝向:

"""根据地球磁场设置ROV的偏航角度"""
import sys
import timefrom pymavlink import mavutil
from move.action import *
from pid_catalog.read import *
from move.arm_disarm import arm,disarm
from move.init import *
from visual.door import *class ATTITUDE_PID():def __init__(self, dt, max, min, Kp, Kd, Ki):self.dt = dt    # 循环时长self.max = max  # 操作变量最大值self.min = min  # 操作变量最小值self.Kp = Kp         # 比例增益self.Kd = Kd         # 积分增益self.Ki = Ki         # 微分增益self.integral = 0    # 直到上一次的误差值self.pre_error = 0   # 上一次的误差值def calculate(self, setPoint, pv):# 其中 pv:process value 即过程值,error = setPoint  -   pv      # 误差Pout = self.Kp * error          # 比例项self.integral += error * self.dtIout = self.Ki * self.integral  # 积分项derivative = (error - self.pre_error)/self.dtDout = self.Kd * derivative     # 微分项output =1500+ Pout + Iout + Dout     # 新的目标值if(output > self.max):output = self.maxelif(output < self.min):output = self.minself.pre_error = error         # 保存本次误差,以供下次计算return output
attitude_pid = ATTITUDE_PID(0.1, 1540, 1460, 5, 0 , 0) # 这个是角度制的,正北方向是0°和360°,忘记是顺时针还是逆时针递增了
def set_target_attitude(attitude,Boolean):master = mavutil.mavlink_connection('udpin:0.0.0.0:14550')while Boolean:msg = master.recv_match()if not msg:continueif msg.get_type() == 'VFR_HUD':print(msg.heading)a=attitude_pid.calculate(attitude,msg.heading)b=int(a)print('b: ',b)yaw(b)if __name__=='__main__':master = mavutil.mavlink_connection('udpin:0.0.0.0:14550')master.wait_heartbeat()disarm()init()arm() #解锁set_target_attitude(360,True)   #让ROV朝正北方向转

17、发送全球定位系统位置

import time
from pymavlink import mavutil
master = mavutil.mavlink_connection('udpin:0.0.0.0:14550')
master.wait_heartbeat()while True:time.sleep(0.2)master.mav.gps_input_send(0,0,(mavutil.mavlink.GPS_INPUT_IGNORE_FLAG_VEL_HORIZ |mavutil.mavlink.GPS_INPUT_IGNORE_FLAG_VEL_VERT |mavutil.mavlink.GPS_INPUT_IGNORE_FLAG_SPEED_ACCURACY),0,0,3,0,0,0,1,1,0,0,0,0,0,0,7)

18、附上我之前获取信息记录的日志文件msg.log,这刚好是一个周期,下次仍从VFR_HUD开始。

日志记录的时间为: 2022-05-10 14:51:51
VFR_HUD {airspeed : 0.0, groundspeed : 0.07441851496696472, heading : 342, throttle : 0, alt : -0.019999999552965164, climb : -0.0017754812724888325}
SERVO_OUTPUT_RAW {time_usec : 1032604820, port : 0, servo1_raw : 1500, servo2_raw : 1500, servo3_raw : 1500, servo4_raw : 1500, servo5_raw : 1500, servo6_raw : 1500, servo7_raw : 0, servo8_raw : 0, servo9_raw : 0, servo10_raw : 1100, servo11_raw : 0, servo12_raw : 0, servo13_raw : 0, servo14_raw : 0, servo15_raw : 0, servo16_raw : 0}
RC_CHANNELS {time_boot_ms : 1032607, chancount : 0, chan1_raw : 1500, chan2_raw : 1500, chan3_raw : 1500, chan4_raw : 1500, chan5_raw : 1500, chan6_raw : 1500, chan7_raw : 1500, chan8_raw : 1500, chan9_raw : 1100, chan10_raw : 1100, chan11_raw : 1100, chan12_raw : 0, chan13_raw : 0, chan14_raw : 0, chan15_raw : 0, chan16_raw : 0, chan17_raw : 0, chan18_raw : 0, rssi : 0}
RAW_IMU {time_usec : 1032607166, xacc : 25, yacc : -18, zacc : -996, xgyro : 0, ygyro : -4, zgyro : -36, xmag : 119, ymag : 161, zmag : 334, id : 0, temperature : 0}
SCALED_IMU2 {time_boot_ms : 1032607, xacc : -20, yacc : -39, zacc : -976, xgyro : -2, ygyro : 7, zgyro : 9, xmag : 0, ymag : 0, zmag : 0, temperature : 0}
SCALED_PRESSURE {time_boot_ms : 1032607, press_abs : 524.38818359375, press_diff : 0.0, temperature : 4625, temperature_press_diff : 0}
SCALED_PRESSURE2 {time_boot_ms : 1032607, press_abs : 1022.3999633789062, press_diff : 0.0, temperature : 2442, temperature_press_diff : 0}
SYSTEM_TIME {time_unix_usec : 0, time_boot_ms : 1032609}
AHRS {omegaIx : 0.0005717225722037256, omegaIy : 0.0045538474805653095, omegaIz : 0.04834114387631416, accel_weight : 0.0, renorm_val : 0.0, error_rp : 0.001656720181927085, error_yaw : 0.020652037113904953}
AHRS2 {roll : 0.022335169836878777, pitch : -0.034166205674409866, yaw : -0.8668319582939148, altitude : 0.0, lat : 0, lng : 0}
AHRS3 {roll : -0.004018092527985573, pitch : 0.014819731004536152, yaw : -0.29812362790107727, altitude : 0.0, lat : 0, lng : 0, v1 : 0.0, v2 : 0.0, v3 : 0.0, v4 : 0.0}
HWSTATUS {Vcc : 4935, I2Cerr : 0}
MOUNT_STATUS {target_system : 0, target_component : 0, pointing_a : 0, pointing_b : 0, pointing_c : 0, mount_mode : 0}
EKF_STATUS_REPORT {flags : 421, velocity_variance : 0.0, pos_horiz_variance : 0.014007089659571648, pos_vert_variance : 0.001747242291457951, compass_variance : 0.6115934252738953, terrain_alt_variance : 0.0, airspeed_variance : 0.0}
VIBRATION {time_usec : 1032615036, vibration_x : 0.025633109733462334, vibration_y : 0.03569412976503372, vibration_z : 0.024316098541021347, clipping_0 : 0, clipping_1 : 0, clipping_2 : 0}
BATTERY_STATUS {id : 0, battery_function : 0, type : 0, temperature : 32767, voltages : [16226, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535], current_battery : 69, current_consumed : 197, energy_consumed : 115, battery_remaining : -1, time_remaining : 0, charge_state : 0, voltages_ext : [0, 0, 0, 0], mode : 0, fault_bitmask : 0}
NAMED_VALUE_FLOAT {time_boot_ms : 1032615, name : CamTilt, value : 0.5}
NAMED_VALUE_FLOAT {time_boot_ms : 1032615, name : CamPan, value : 0.5}
NAMED_VALUE_FLOAT {time_boot_ms : 1032615, name : TetherTrn, value : 0.0}
NAMED_VALUE_FLOAT {time_boot_ms : 1032615, name : Lights1, value : 0.0}
NAMED_VALUE_FLOAT {time_boot_ms : 1032615, name : Lights2, value : 0.5}
NAMED_VALUE_FLOAT {time_boot_ms : 1032615, name : PilotGain, value : 0.5}
NAMED_VALUE_FLOAT {time_boot_ms : 1032615, name : InputHold, value : 0.0}
ATTITUDE {time_boot_ms : 1032701, roll : -0.004012368619441986, pitch : 0.014825592748820782, yaw : -0.2980099320411682, rollspeed : 0.000885059533175081, pitchspeed : 0.0003427495248615742, yawspeed : 0.012221965938806534}
GLOBAL_POSITION_INT {time_boot_ms : 1032701, lat : 0, lon : 0, alt : -20, relative_alt : -25, vx : 0, vy : 7, vz : 0, hdg : 34293}
SYS_STATUS {onboard_control_sensors_present : 35716111, onboard_control_sensors_enabled : 35691535, onboard_control_sensors_health : 36740111, load : 404, voltage_battery : 16234, current_battery : 69, battery_remaining : -1, drop_rate_comm : 0, errors_comm : 0, errors_count1 : 0, errors_count2 : 0, errors_count3 : 0, errors_count4 : 0}
POWER_STATUS {Vcc : 4933, Vservo : 4993, flags : 4}
MEMINFO {brkval : 0, freemem : 65535, freemem32 : 127776}
NAV_CONTROLLER_OUTPUT {nav_roll : -0.2298368662595749, nav_pitch : 0.8494492173194885, nav_bearing : -17, target_bearing : 0, wp_dist : 0, alt_error : 0.029539374634623528, aspd_error : 0.0, xtrack_error : 0.0}
MISSION_CURRENT {seq : 0}

3.2.3 如何使用Pymavlink来控制BlueROV运动(以巡线为例)

主函数思路:
1、首先需要将BlueROV解锁(比如在完成一些导包后调用前面的arm()函数使ROV解锁)
2、调整相机云台的朝向(巡线肯定是要向下看)
3、可以考虑创建两个线程以保证信息处理的实时性:
(1)视觉线程:
通过视觉代码把管道的中心点横坐标进行归一化,其中像素坐标系最左边是-1,最后边是1,中间是0,因此最终值 x∈[-1, 1]。
(2)动作线程:
其中动作包括两部分:
第一需要考虑到稳深控制,ROV本身的稳深模式“ALT_HOLD”不好用,可以自己读取深度计 并通过PID控制ROV的深度。
第二需要考虑到方向控制,可以用视觉返回的x通过PID计算出需要输出的pwm,进而对ROV的转向进行控制。
两个线程通过队列(queue)进行通信。
4、巡线结束后把机器人给锁上(使用前面封装好的disarm()函数锁上机器人)

**提示:**可以创建一个ROV类,把ROV需要用到的函数和参数写进去,面向对象。

4 工作过程

4.1 ROV下水前的准备

1.安装电池
2.测量电池仓和控制仓的气密性
3.安装通气螺栓并检查是否是否拧紧
4.检查螺旋桨附近是否有异物

4.2 ROV下水后进行观察

观察ROV是否出现了前后不平、左右不平的状况,如果出现这种状况,应该对ROV采用添加浮力块或者增加配重的方法对ROV进行调平,否则ROV可能会在运动的过程中出现运动不稳定的现象,不能够平稳运行。

4.3 ROV上岸前

将机器开回回收点,一定将机器锁上,禁止将手伸进ROV螺旋桨里面!!!

4.4 ROV上岸后

用毛巾把电池仓附近的水擦干,拧出通气螺栓,然后再拔出电池仓后盖并取出电池,电池快没电的话就对电池进行充电。

5 常见问题(目前想到的)

1、在自主代码调试过程中ROV在水中出现失控乱动的情况
首先不要慌张,安全要紧!!!如果是有缆调试的话“CTRL+C”结束进程,如果行不通的话请打开QGC,然后用QGC把机器锁上。

原理:机器只能受QGC或者代码一个进程控制,当QGC启动的时候,机器自动脱离代码的控制。

2、机器出现动力不足,无法正常运动
检查机器螺旋桨是否缠到异物以及电池是否快要没电了。
3、在调试代码的时候发现机器没有按照代码控制进行运动(代码确信正确),而是待在原地静止不动
1、如果之前打开过QGC的话,检查QGC是否关闭
2、很有可能是机器触发了故障保护机制,当机器接收信息的频率过低或者阻塞时,BlueROV会触发其自身的故障保护,防止ROV在不受人类控制下走丢。有兴趣的同学可参考我主页上的问题 I couldn’t connect to the ROV after resuming the thread。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/134551.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小白学爬虫:通过关键词搜索1688商品列表数据接口|1688商品列表数据接口|1688商品列表数据采集|1688API接口

通过关键词搜索1688商品列表数据接口可以使用1688开放平台提供的API接口实现。以下是使用关键词搜索商品列表数据的基本步骤&#xff1a; 1、注册并获取AppKey。 2、构造请求参数&#xff0c;包括搜索关键词、页码、每页条数等。 3、通过API接口链接&#xff0c;将请求参数发送…

【广州华锐互动】气象卫星监测AR互动教学软件为气象学习带来更多乐趣

由VR制作公司广州华锐互动开发的气象卫星监测AR互动教学软件是一款结合了增强现实(AR)技术与气象监测技术的教育软件。它通过直观、互动的方式&#xff0c;帮助学生更好地理解和掌握气象监测的基本知识和技能。本文将从气象卫星监测AR互动教学软件的应用场景、优势分析、实际意…

【SVN】

SVN 1 svn使用1.1 主干合并到分支1.2 分支合并到主干1.3 分支建立1.4 创建分支1.5 切换分支1.6 合并分支1.7 删除分支 2 概念理解 1 svn使用 1.1 主干合并到分支 首先&#xff0c;在本地trunk中先update一下&#xff0c;有冲突的解决冲突&#xff0c;保证trunk和repository已…

初步了解 RabbitMQ

目录 ​编辑一、MQ 概述 1、MQ 的简介 2、MQ 的用途 &#xff08;1&#xff09;限流削峰 &#xff08;2&#xff09;异步解耦 (3)数据收集 二、RabbitMQ 概述 1、RabbitMQ 简介 2、四大核心概念 3、RabbitMQ 的核心部分 ​编辑 4、名词解释&#xff1a; 三、Hello …

用C++编写动画+音频版极乐净土

#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<graphics.h> #include<Windows.h> #include<MMSystem.h>//播放音乐所需要的头文件 #pragma comment(lib,"winmm.lib")//告诉编译器&#xff0c;加载winmm.lib库文件 #define C…

[黑马程序员Pandas教程]——Pandas缺失值处理

目录&#xff1a; 学习目标空值和缺失值查看缺失值 加载数据并通过info函数初步查看缺失值情况df.isnull().sum()空值数量统计Missingno库对缺失值的情况进行可视化探查 安装missingno库missingno.bar(df)缺失值数量可视化missingno.matrix(df)缺失值位置的可视化missingno.he…

线扫相机DALSA软件开发套件有哪些

Win10和Win7系统完整SDK目录截图&#xff1a; Sapera Configuration 缓存与内存管理&#xff0c;以及通信端口配置工具&#xff0c;部分功能等效于Detection(查找相机)内的Settings。 Sapera Log Viewer 打开Log Viewer后会显示之前发生过的所有与Sapera LT软件有关的运行信息…

15套前端经典实战项目大合集,小白练手必备实战项目

15套前端经典实战项目大合集&#xff0c;悄悄练习&#xff0c;你会惊艳所有人。 今日我以内卷为荣&#xff0c;明日内卷以我为荣&#xff0c;不管学习哪门语言都要做出实际的东西来&#xff0c;这个实际的东西就是项目。 这里整理了15前端经典实战项目&#xff0c;每套都有完…

Redis 扩展 RedisBloom 插件,解决缓存击穿、穿透

文章目录 一、概述二、编译准备2.1 升级 make2.2 安装 Python3 三、编译 RedisBloom四、测试 RedisBloom五、应用场景5.1 缓存击穿5.2 缓存穿透5.3 原理总结 六、存在的问题 如果您对Redis的了解不够深入请关注本栏目&#xff0c;本栏目包括Redis安装&#xff0c;Redis配置文件…

使用CMake引入第三方so库及头文件并调用头文件声明的函数

首先,要调用别人的so库和头文件,我们自己项目中需要有NDK。 因为只有C++代码才能直接调用C++代码,也就是头文件和so库的函数。 其次,就是要想办法把头文件,so库和项目中的NDK关联起来,然后作为一个整体,生成一个jni,供Java层调用。 最后,二者的关联是通过CMake完成的…

软件测试/测试开发丨接口测试Mock实战练习学习笔记

点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接&#xff1a;https://ceshiren.com/t/topic/27857 一、Rewrite 1.1、Rewrite 原理 1.2、Rewrite 实战 Tools → Rewrite 勾选 Enable Rewrite 点击下方 Add 按钮新建一个重写的规则 在右侧编辑重…

使用PCtoLCD2002提取字模

“模式”---“字符模式” LCD显示&#xff0c;汉字使用宋体还是比较好的&#xff0c;16*16是长、宽都是16个像素显示。

Spark Core

Spark Core 本文来自 B站 黑马程序员 - Spark教程 &#xff1a;原地址 第一章 RDD详解 1.1 为什么需要RDD 分布式计算需要 分区控制shuffle控制数据存储、序列化、发送数据计算API等一系列功能 这些功能&#xff0c;不能简单的通过Python内置的本地集合对象&#xff08;如…

gitlab 设置 分支只读

一&#xff0c;设置master分支只读&#xff0c; 并且只有Maintainers 拥有合并权限。 二&#xff0c;设置成员权限 改为developer 三&#xff0c;邀请成员 点击右上角 Invite Members

AVS3:双向梯度修正BGC

双向梯度修正&#xff08;Bi-directional Gradient Correction&#xff0c;BGC&#xff09;是利用双向参考块间的差值对预测值进行修正的技术。 BGC仅用于双向预测CU&#xff0c;设两个方向得到的单向预测值分别为pred0和pred1&#xff0c;修正前的双向预测值为predBI&#xf…

web —— html

Web —— css基础 1. HTML2. 基本HTML结构3. HTML常用标签3.1 文本相关标签3.2 HTML图像标签3.3 HTML超链接标签3.4 HTML表&#xff0c;单3.4.1 HTML表格3.4.2 HTML表单&#xff0c;输入框&#xff08;多选框&#xff0c;单选框&#xff09;下拉框 3.5 HTML分区标签3.5.1 div标…

LeetCode-94. 二叉树的中序遍历(C++)

目录捏 一、题目描述二、示例与提示三、思路四、代码 一、题目描述 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 二、示例与提示 示例 1&#xff1a; 输入&#xff1a; root [1,null,2,3] 输出&#xff1a; [1,3,2] 示例 2&#xff1a; 输入&#xf…

大数据毕业设计选题推荐-营业厅营业效能监控平台-Hadoop-Spark-Hive

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

初识Java 17-2 反射

目录 转型前检查 构建例子&#xff1a;生成层次结构 优化Creator&#xff1a;使用类字面量 优化PetCounter&#xff1a;动态验证类型 更通用的递归计数 注册工厂 本笔记参考自&#xff1a; 《On Java 中文版》 转型前检查 当我们使用传统的类型转换&#xff0c;例如&…

支付卡行业(PCI)PIN安全要求和测试程序 7个控制目标、33个要求及规范性附录ABC 密钥注入-PCI认证-安全行业基础篇4

概述 用于在ATM和POS终端进行在线和离线支付卡交易处理期间&#xff0c;对个人身份号码&#xff08;PIN&#xff09;数据进行安全管理、处理和传输。 该标准具体包括 7 个控制目标和 33 个安全要求&#xff0c; 标准的结构分为标准主体部分&#xff0c;标准附录&#xff08;N…