Documentation
图像相关
📘 ImageProtocolManager是一个用于处理MAVLink图像传输协议的类,主要用于接收和处理来自光流相机等设备的图像数据。以下是其主要工作流程:
1. 基本组成
- 两个主要成员变量:
_imageHandshake: 存储图像传输握手信息 
_imageBytes: 存储接收到的图像数据 
 
2. 图像接收流程
- 握手阶段
 
1
   | case MAVLINK_MSG_ID_DATA_TRANSMISSION_HANDSHAKE:
   | 
 
- 接收
DATA_TRANSMISSION_HANDSHAKE消息 
- 清空之前的图像数据
 
- 记录新图像的基本信息(类型、宽度、高度等)
 
- 数据传输阶段
 
1
   | case MAVLINK_MSG_ID_ENCAPSULATED_DATA:
   | 
 
- 接收
ENCAPSULATED_DATA消息 
- 根据序号(
seqnr)将数据放入正确的位置 
- 通过
_imageHandshake.packets计数追踪传输完成情况 
- 当所有数据包接收完成时,发出
imageReady信号 
- 图像获取
 
1
   | QImage ImageProtocolManager::getImage(void)
   | 
 
支持多种图像格式:
- RAW8U/RAW32U:构建PGM格式
 
- BMP/JPEG/PGM/PNG:直接加载
 
- 返回QImage对象
 
3. 使用示例
假设您要在代码中使用这个类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   |  ImageProtocolManager* imageManager = new ImageProtocolManager();
 
  connect(imageManager, &ImageProtocolManager::imageReady, this, [=]() {     QImage image = imageManager->getImage();      });
 
  void onMavlinkMessageReceived(mavlink_message_t message) {     imageManager->mavlinkMessageReceived(message); }
 
 
  | 
 
4. 错误处理
该类包含多个错误检查:
- 检查是否有未完成的前序传输
 
- 验证数据包序号是否有效
 
- 检查图像数据完整性
 
- 支持的图像格式验证
 
5. 注意事项
- 图像传输必须按照顺序:先握手,后数据
 
- 在调用
getImage()之前确保图像传输完成 
- 支持多种图像格式,但对于RAW格式需要特殊处理
 
这个类的设计遵循了MAVLink图像传输协议规范
- ImageProtocolManager的局限性
 
- ImageProtocolManager只是一个接收和处理图像数据的工具
 
- 它不能主动触发拍照操作
 
- 它只负责接收和处理MAVLink传输的图像数据
 
- 正确的拍照流程
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
   | class PhotoCapture : public QObject { public:     PhotoCapture(Vehicle* vehicle) {                  _cameraControl = vehicle->cameraManager()->currentCameraInstance();
                   _imageManager = new ImageProtocolManager();
                   connect(vehicle, &Vehicle::mavlinkMessageReceived,                 _imageManager, &ImageProtocolManager::mavlinkMessageReceived);
                   connect(_imageManager, &ImageProtocolManager::imageReady,                 this, &PhotoCapture::handleImageReceived);     }
      void capturePhoto() {         if (_cameraControl) {             _cameraControl->takePhoto();           }     }
  private slots:     void handleImageReceived() {         QImage image = _imageManager->getImage();         if (!image.isNull()) {             QString filename = QString("photo_%1.jpg")                 .arg(QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss"));             image.save(filename);         }     }
  private:     QGCCameraControl* _cameraControl;     ImageProtocolManager* _imageManager; };
 
  | 
 
- 为什么需要相机控制
 
- 相机需要接收拍照命令才会开始拍照
 
- 相机控制器处理相机的状态和参数设置
 
- 相机控制器提供拍照反馈和状态更新
 
- 完整的最小实现
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
   |  class MinimalPhotoCapture : public QObject { public:     MinimalPhotoCapture(Vehicle* vehicle) : _vehicle(vehicle) {                  _imageManager = new ImageProtocolManager();
                   connect(_vehicle, &Vehicle::mavlinkMessageReceived,                 _imageManager, &ImageProtocolManager::mavlinkMessageReceived);
          connect(_imageManager, &ImageProtocolManager::imageReady,                 this, &MinimalPhotoCapture::onImageReady);     }
      void takePhoto() {                  if (auto camera = _vehicle->cameraManager()->currentCameraInstance()) {             camera->takePhoto();         }     }
  private slots:     void onImageReady() {                  QImage image = _imageManager->getImage();         if (!image.isNull()) {             image.save("captured_photo.jpg");             qDebug() << "Photo saved successfully";         }     }
  private:     Vehicle* _vehicle;     ImageProtocolManager* _imageManager; };
 
 
  | 
 
- 使用示例
 
1 2 3 4 5 6 7
   |  Vehicle* vehicle = ...;   MinimalPhotoCapture* photoCapture = new MinimalPhotoCapture(vehicle);
 
  photoCapture->takePhoto();
 
 
  | 
 
所以总结来说:
- 不能只使用ImageProtocolManager来完成拍照
 
- 需要通过相机控制器发送拍照命令
 
- ImageProtocolManager负责接收和处理图像数据
 
- 两者配合才能完成完整的拍照流程
 
建议:
- 如果只需要简单拍照功能,可以使用上面的MinimalPhotoCapture实现
 
- 如果需要更多相机控制功能(如设置参数、模式等),则使用完整的相机控制系统
 
- 始终需要同时使用相机控制和ImageProtocolManager
 
多智能体
MultiVehicleManager是一个核心管理类,负责管理和协调多个车辆(Vehicle)。
1. 核心功能
- 车辆管理
 
1 2 3 4 5 6 7
   | class MultiVehicleManager : public QGCTool { private:     QList<Vehicle*>    _vehicles;                Vehicle*           _activeVehicle;           bool              _activeVehicleAvailable; };
 
  | 
 
- 通信管理
 
1 2 3 4 5 6 7 8 9 10 11 12
   | void MultiVehicleManager::_sendGCSHeartbeat(void) {          for (int i=0; i<sharedLinks.count(); i++) {         LinkInterface* link = sharedLinks[i].get();         if (link->isConnected()) {             mavlink_message_t message;             mavlink_msg_heartbeat_pack_chan(...);             link->writeBytesThreadSafe((const char*)buffer, len);         }     } }
 
  | 
 
- 编队控制协调
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
   | void MultiVehicleManager::_mainWork(void) {          switch(bs_multi_model) {         case Single:                          break;         case Multi8:                          if(multi_start_flag) {                                  for(int i = 0; i<_vehicles.count(); i++) {                     Vehicle *v = qobject_cast<Vehicle*>(_vehicles[i]);                     LBAgent agent;                     agent.id = v->id();                     agent.lat = v->lat;                     agent.lon = v->lon;                                          _allagent.push_back(agent);                 }
                                   vector<LBActor> all_actor =                     _multiAgentController.FaultFormationControl2(_allagent, formation_gap);
                                   for(int i = 0; i<_vehicles.count(); i++) {                     Vehicle *v = qobject_cast<Vehicle*>(_vehicles[i]);                     v->receiveMultiParameters(control_data);                 }             }             break;     } }
 
  | 
 
2. 关键职责
- 状态管理
 
1 2 3 4 5 6
   |  bool _manipulate_connect_flag;   bool _logic_connect_flag;        bool _virtual_connect_flag;      bool multi_start_flag;          
 
 
  | 
 
- 任务协调
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
   | void MultiVehicleManager::setMissionItems(const QList<MissionItem*>& items) {     _allMissionItems = items;     set_mission_flag = true;
           vector<LBMission> missions;     for(int i = 0; i<_allMissionItems.count(); i++) {         MissionItem* item = _allMissionItems[i];         LBMission mis;         mis.lat = item->param5();         mis.lon = item->param6();         missions.push_back(mis);     }     _multiAgentController.MissionGet(missions); }
 
  | 
 
- 模式切换管理
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
   | void MultiVehicleManager::switchControlMode(ControlMode mode) {     bs_control_model = mode;
           if(_manipulate_connect_flag) manipulateDisconnect();     if(_logic_connect_flag) logicDisconnect();     if(_virtual_connect_flag) virtualDisconnect();
           switch(mode) {         case Manipulate:             manipulateConnect();             break;         case Logic:             logicConnect();             break;         case Virtual:             virtualConnect();             break;     } }
 
  | 
 
3. 使用示例
- 初始化多船系统
 
1 2 3 4 5 6 7 8 9
   |  MultiVehicleManager* manager = new MultiVehicleManager(app, toolbox);
 
  manager->setMultiModel(Multi8);
 
  manager->setControlModel(Manipulate);
 
 
  | 
 
- 执行编队任务
 
1 2 3 4 5 6 7 8
   |  QList<MissionItem*> missionItems;
  manager->setMissionItems(missionItems);
 
  manager->startMultiFormation();
 
 
  | 
 
4. 重要接口
- 车辆管理接口
 
1 2 3 4
   | Vehicle* getVehicleById(int vehicleId); Vehicle* activeVehicle(); QList<Vehicle*> vehicles();
 
   | 
 
- 控制接口
 
1 2 3 4 5
   | void setMultiModel(MultiModel model); void setControlModel(ControlModel model); void startMultiFormation(); void stopMultiFormation();
 
   | 
 
- 状态监控接口
 
1 2 3 4 5
   | bool isMultiFormationActive(); bool isVehicleAvailable(); void vehicleAdded(Vehicle* vehicle); void vehicleRemoved(Vehicle* vehicle);
 
   | 
 
5. 注意事项
- 确保所有车辆都已正确连接和初始化
 
- 在切换模式前检查当前状态
 
- 处理通信延迟和丢失情况
 
- 保持心跳包的定期发送
 
- 正确处理异常情况和错误恢复
 
MultiVehicleManager是整个多车辆系统的中枢,它协调各个子系统的工作,确保整个系统的正常运行。在进行编队控制时,它扮演着"指挥官"的角色,负责协调各个车辆的行动。
编队控制
1. 核心类的功能
- MultiAgentFormation
 
- 作为编队控制的主要接口类
 
- 管理不同类型的编队控制器
 
- 提供多种编队模式的切换功能
 
- VSFormationControl
 
- 实现基础的编队控制算法
 
- 支持多种编队形状(方形、C字型、纵排、三角形等)
 
- 处理目标点计算和速度控制
 
2. 编队模式
从代码中可以看到多种编队模式:
- 基础编队模式 (VSFormationControl::VSFormationCalculate)
 
1 2 3 4 5 6 7
   | switch(formationModel) {     case 0:      case 1:      case 2:      case 3:  }
 
  | 
 
- 特殊编队模式
 
- HUSTMoveFormation: 移动编队
 
- HUSTSearchFormation: 搜索编队
 
- HUSTExpelFormation: 驱离编队
 
- FaultFormation: 容错编队
 
3. 编队切换方法
要完成编队切换,需要:
- 设置编队模式
 
1 2 3 4 5 6 7 8 9 10
   |  if(bs_multi_model == Multi3) {            control_data.work_model = Mission;     control_data.formation_flag = true;
           all_missions = _multiAgentController.HUSTMoveFormationControl(_allagent, formation_gap); }
 
 
  | 
 
- 切换步骤
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   |  MultiAgentFormation _multiAgentController;
 
  vector<LBMission> missions;
  _multiAgentController.MissionGet(missions);
 
 
  vector<vector<LBMission>> all_missions = _multiAgentController.HUSTMoveFormationControl(_allagent, formation_gap);
 
  all_missions = _multiAgentController.HUSTSearchFormationControl(_allagent, formation_gap);
 
 
  | 
 
4. 编队切换的关键参数
- 控制标志
 
1 2 3 4
   | control_data.work_model     control_data.work_state    control_data.formation_flag 
 
   | 
 
- 编队参数
 
1 2 3 4
   | formation_gap   refreshFlag     multi_start_flag 
 
   | 
 
5. 实际使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
   |  void switchToMoveFormation() {          control_data.work_model = Mission;     control_data.work_state = USVRun;     control_data.formation_flag = true;
           if(set_mission_flag) {         vector<LBMission> missions;                  _multiAgentController.MissionGet(missions);     }
           vector<vector<LBMission>> all_missions =         _multiAgentController.HUSTMoveFormationControl(_allagent, formation_gap);
           for(int i = 0; i < _vehicles.count(); i++) {         Vehicle *v = qobject_cast<Vehicle*>(_vehicles[i]);         if(all_missions.size() > i) {             v->MissionGet(convertToMissionItems(all_missions[i]));         }         v->receiveMultiParameters(control_data);     } }
 
  | 
 
直接通过grabImage()方法实现
可以直接使用VideoManager的grabImage()方法来获取图片。这个方法相对简单,不需要直接操作摄像头控制。
1. grabImage() 方法分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   | void VideoManager::grabImage(const QString& imageFile) {          if (!_videoReceiver[0] && !_videoReceiver[2]) {         return;     }
           if (imageFile.isEmpty()) {                  _imageFile = qgcApp()->toolbox()->settingsManager()->appSettings()->photoSavePath();         _imageFile += "/" + QDateTime::currentDateTime().toString("yyyy-MM-dd_hh.mm.ss.zzz") + ".jpg";     } else {         _imageFile = imageFile;     }
      emit imageFileChanged();
           _videoReceiver[0]->takeScreenshot(_imageFile);     _videoReceiver[2]->takeScreenshot(_imageFile); }
 
   | 
 
2. 使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   |  void MultiVehicleManager::captureImage() {     VideoManager* videoManager = qgcApp()->toolbox()->videoManager();     if (videoManager) {                  videoManager->grabImage();
                   QString customPath = "path/to/your/photo.jpg";         videoManager->grabImage(customPath);     } }
 
 
  | 
 
4. 使用注意事项
- 确保视频流已启动
 
1 2 3 4
   | if (!videoManager->streaming()) {     videoManager->startVideo(); }
 
  | 
 
- 检查保存路径
 
1 2 3 4 5 6
   | QString photoPath = qgcApp()->toolbox()->settingsManager()->appSettings()->photoSavePath(); QDir dir(photoPath); if (!dir.exists()) {     dir.mkpath("."); }
 
   | 
 
- 处理错误情况
 
1 2 3 4 5 6 7 8 9
   | connect(videoManager, &VideoManager::imageFileChanged, this, [=]() {     QString imageFile = videoManager->imageFile();     if (QFile::exists(imageFile)) {         qDebug() << "Image saved successfully:" << imageFile;     } else {         qWarning() << "Failed to save image";     } });
 
  | 
 
5. 建议的完整实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
   | class PhotoCapture { public:     PhotoCapture() {         _videoManager = qgcApp()->toolbox()->videoManager();         connect(_videoManager, &VideoManager::imageFileChanged,                 this, &PhotoCapture::_handleImageCaptured);     }
      void capturePhoto(const QString& customPath = QString()) {         if (!_videoManager) return;
                   if (!_videoManager->streaming()) {             _videoManager->startVideo();                          QTimer::singleShot(1000, this, [=]() {                 _videoManager->grabImage(customPath);             });         } else {             _videoManager->grabImage(customPath);         }     }
  private:     void _handleImageCaptured() {         QString imageFile = _videoManager->imageFile();         if (QFile::exists(imageFile)) {             emit photoCaptured(imageFile);         } else {             emit photoCaptureFailed();         }     }
      VideoManager* _videoManager;
  signals:     void photoCaptured(const QString& path);     void photoCaptureFailed(); };
 
  | 
 
这种方式比直接控制相机要简单得多,适合只需要获取当前视频画面的场景。