ArduPilot开源飞控之AP_Mount_Siyi

ArduPilot开源飞控之AP_Mount_Siyi

  • 1. 源由
  • 2. 框架设计
    • 2.1 类和继承
    • 2.2 公共方法
    • 2.3 保护方法
    • 2.4 私有成员和方法
    • 2.5 解析状态
    • 2.6 重要成员变量
  • 3. 重要方法
    • 3.1 AP_Mount_Siyi::init
    • 3.2 AP_Mount_Siyi::update
    • 3.3 AP_Mount_Siyi::read_incoming_packets
    • 3.4 AP_Mount_Siyi::process_packet
    • 3.5 AP_Mount_Siyi::update_zoom_control
    • 3.6 AP_Mount_Backend::set_rctargeting_on_rcinput_change
  • 4. 辅助函数
    • 4.1 基本能力
    • 4.2 摄像头&红外测距功能
    • 4.3 云台操作
  • 4. 总结
  • 5. 参考资料

1. 源由

AP_Mount_Siyi主要是挂载SIYI云台,配合SIYI的产品,比如相机。

  • SIYI Gimbal Camera

目前,SIYI云台摄像头还是挺不错的,而且在开源社区也是蛮热门的。另外,有专业的团队进行代码维护,相对来说应用代码涉及的内容会更加全面。

2. 框架设计

  • 继承自 AP_Mount_Backend
  • 用于控制摄像头和万向节的类

这个类是一个复杂的云台和摄像头控制系统,提供了初始化、更新、健康检查以及各种摄像头操作(如拍照、录像、变焦、对焦等)的方法。它还定义了一些用于处理和发送数据包的私有方法和变量。

2.1 类和继承

class AP_Mount_Siyi : public AP_Mount_Backend
{
    // 使用基类构造函数
    using AP_Mount_Backend::AP_Mount_Backend;

    // 禁止拷贝构造和赋值
    CLASS_NO_COPY(AP_Mount_Siyi);
  • AP_Mount_Siyi 继承自 AP_Mount_Backend
  • 使用 using 关键字继承了基类的构造函数。
  • 使用宏 CLASS_NO_COPY 禁止类的拷贝构造和赋值操作。

2.2 公共方法

public:
    void init() override;
    void update() override;
    bool healthy() const override;
    bool has_roll_control() const override { return false; }
    bool has_pan_control() const override { return yaw_range_valid(); }
    
    bool take_picture() override;
    bool record_video(bool start_recording) override;
    bool set_zoom(ZoomType zoom_type, float zoom_value) override;
    SetFocusResult set_focus(FocusType focus_type, float focus_value) override;
    bool set_lens(uint8_t lens) override;
    bool set_camera_source(uint8_t primary_source, uint8_t secondary_source) override;
    void send_camera_information(mavlink_channel_t chan) const override;
    void send_camera_settings(mavlink_channel_t chan) const override;
    bool get_rangefinder_distance(float& distance_m) const override;
  • initupdatehealthy 等方法是重载基类的虚函数。
  • has_roll_control 返回 false,表示该设备不支持滚动控制。
  • has_pan_control 返回 yaw_range_valid() 的结果,表示是否支持平移控制。

2.3 保护方法

protected:
    bool get_attitude_quaternion(Quaternion& att_quat) override;
    bool get_angular_velocity(Vector3f& rates) override {
        rates = _current_rates_rads;
        return true;
    }
  • get_attitude_quaternion 获取姿态四元数。
  • get_angular_velocity 获取角速度,这里直接返回 _current_rates_rads

2.4 私有成员和方法

private:
    enum class SiyiCommandId { ... };
    enum class FunctionFeedbackInfo : uint8_t { ... };
    enum class PhotoFunction : uint8_t { ... };
    enum class ParseState : uint8_t { ... };
    enum class HardwareModel : uint8_t { ... } _hardware_model;
    enum class HdrStatus : uint8_t { OFF = 0, ON = 1 };
    enum class RecordingStatus : uint8_t { OFF = 0, ON = 1, NO_CARD = 2, DATA_LOSS = 3 };
    enum class GimbalMotionMode : uint8_t { LOCK = 0, FOLLOW = 1, FPV = 2 };
    enum class GimbalMountingDirection : uint8_t { UNDEFINED = 0, NORMAL = 1, UPSIDE_DOWN = 2 };
    enum class VideoOutputStatus : uint8_t { HDMI = 0, CVBS = 1 };
    
    typedef struct { ... } GimbalConfigInfo;
    static_assert(sizeof(GimbalConfigInfo) == 7, "GimbalConfigInfo must be 7 bytes");
    
    enum class CameraImageType : uint8_t { ... };

    typedef struct { ... } Version;
    typedef struct { ... } FirmwareVersion;

    void read_incoming_packets();
    void process_packet();
    bool send_packet(SiyiCommandId cmd_id, const uint8_t* databuff, uint8_t databuff_len);
    bool send_1byte_packet(SiyiCommandId cmd_id, uint8_t data_byte);
    void request_firmware_version();
    void request_hardware_id();
    void request_configuration();
    void request_function_feedback_info();
    void request_gimbal_attitude();
    void request_rangefinder_distance();
    void rotate_gimbal(int8_t pitch_scalar, int8_t yaw_scalar, bool yaw_is_ef);
    bool set_motion_mode(const GimbalMotionMode mode, const bool force=false);
    void send_target_rates(float pitch_rads, float yaw_rads, bool yaw_is_ef);
    void send_target_angles(float pitch_rad, float yaw_rad, bool yaw_is_ef);
    bool send_zoom_rate(float zoom_value);
    bool send_zoom_mult(float zoom_mult);
    float get_zoom_mult_max() const;
    void update_zoom_control();
    const char* get_model_name() const;
    void check_firmware_version() const;
    
    AP_HAL::UARTDriver *_uart;
    bool _initialised;
    bool _got_hardware_id;
    FirmwareVersion _fw_version;
    uint8_t _msg_buff[AP_MOUNT_SIYI_PACKETLEN_MAX];
    uint8_t _msg_buff_len;
    const uint8_t _msg_buff_data_start = 8;
    
    struct PACKED {
        uint16_t data_len;
        uint8_t command_id;
        uint16_t data_bytes_received;
        uint16_t crc16;
        ParseState state;
    } _parsed_msg;

    uint32_t _last_send_ms;
    uint16_t _last_seq;
    Vector3f _current_angle_rad;
    Vector3f _current_rates_rads;
    uint32_t _last_current_angle_rad_ms;
    uint32_t _last_req_current_angle_rad_ms;
    ZoomType _zoom_type;
    float _zoom_rate_target;
    float _zoom_mult;
    uint32_t _last_zoom_control_ms;
    GimbalConfigInfo _config_info;
    uint32_t _last_rangefinder_req_ms;
    uint32_t _last_rangefinder_dist_ms;
    float _rangefinder_dist_m;
    uint32_t _last_attitude_send_ms;

    void send_attitude(void);

    struct HWInfo {
        uint8_t hwid[2];
        const char* model_name;
    };
    static const HWInfo hardware_lookup_table[];
    
    uint8_t sent_time_count;
};
  • 枚举 SiyiCommandIdFunctionFeedbackInfo 等定义了各种命令和状态。
  • GimbalConfigInfo 结构体存储云台配置信息,并用 static_assert 确保其大小为 7 字节。
  • 各种私有方法用于发送、处理和读取数据包,管理云台的运动模式和摄像头的控制。

2.5 解析状态

enum class ParseState : uint8_t {
    WAITING_FOR_HEADER_LOW,
    WAITING_FOR_HEADER_HIGH,
    WAITING_FOR_CTRL,
    WAITING_FOR_DATALEN_LOW,
    WAITING_FOR_DATALEN_HIGH,
    WAITING_FOR_SEQ_LOW,
    WAITING_FOR_SEQ_HIGH,
    WAITING_FOR_CMDID,
    WAITING_FOR_DATA,
    WAITING_FOR_CRC_LOW,
    WAITING_FOR_CRC_HIGH,
};
  • ParseState 枚举表示数据包解析的不同状态。

2.6 重要成员变量

  • _uart:指向用于通信的 UART 驱动程序。
  • _initialised:标志驱动程序是否已初始化。
  • _got_hardware_id:标志是否已收到硬件 ID。
  • _fw_version:固件版本信息。
  • _msg_buff_msg_buff_len:用于存储消息缓冲区和其长度。
  • _parsed_msg:结构体,用于存储解析消息的状态。
  • _current_angle_rad_current_rates_rads:存储从云台接收到的当前角度和角速度。

3. 重要方法

3.1 AP_Mount_Siyi::init

该初始化例程在AP_Mount::init中调用。

AP_Mount_Siyi::init()
├── 定义 `serial_manager` 变量,并获取 `AP_SerialManager` 的实例
│   ├── `const AP_SerialManager& serial_manager = AP::serialmanager();`
├── 通过 `serial_manager` 查找串口并赋值给 `_uart`
│   ├── `_uart = serial_manager.find_serial(AP_SerialManager::SerialProtocol_Gimbal, 0);`
│   ├── 检查 `_uart` 是否为 `nullptr`
│       ├── 如果是 `nullptr`
│       │   ├── `return;`
├── 设置 `_initialised` 为 `true`
│   ├── `_initialised = true;`
├── 调用基类的 `init()` 方法
    ├── `AP_Mount_Backend::init();`

3.2 AP_Mount_Siyi::update

该定时更新例程在AP_Mount::update中调用。

AP_Mount_Siyi::update
├── 检查是否初始化 _initialised
│   ├── 如果未初始化,立即退出
├── 读取云台的传入数据包 read_incoming_packets()
├── 获取当前时间 now_ms
├── 定期请求固件版本或配置
│   ├── 如果自上次发送以来已过1000毫秒
│   │   ├── 更新最后发送时间 _last_send_ms
│   │   ├── 如果未获取硬件ID,则请求硬件ID request_hardware_id()
│   │   ├── 否则如果固件版本未收到,则请求固件版本 request_firmware_version()
│   │   ├── 否则请求配置 request_configuration()
│   │   └── 发送UTC时间给摄像头 (AP_RTC_ENABLED)
│       └── 如果已发送时间计数 sent_time_count 小于 5
│           ├── 获取UTC时间
│           └── 发送时间数据包 send_packet()
├── 定期请求姿态 request_gimbal_attitude()
│   ├── 如果自上次请求姿态以来已过50毫秒
│       ├── 请求云台姿态
│       └── 更新最后请求姿态时间 _last_req_current_angle_rad_ms
├── 定期请求测距仪距离 request_rangefinder_distance()
│   ├── 如果硬件型号为 ZT30 且自上次请求以来已过100毫秒
│       └── 请求测距仪距离
│       └── 更新最后请求时间 _last_rangefinder_req_ms
├── 定期发送姿态 send_attitude()
│   ├── 如果自上次发送姿态以来已过100毫秒
│       ├── 更新最后发送姿态时间 _last_attitude_send_ms
│       └── 发送姿态
├── 运行变焦控制 update_zoom_control()
├── RC输入改变时切换到 RC_TARGETING 模式 set_rctargeting_on_rcinput_change()
├── 获取当前云台模式 get_mode()
│   ├── 根据模式设定目标角度或速率
│       ├── MAV_MOUNT_MODE_RETRACT 模式
│           ├── 获取收回角度 _params.retract_angles
│           ├── 设置目标类型为角度
│           └── 将角度从度转换为弧度并设置
│       ├── MAV_MOUNT_MODE_NEUTRAL 模式
│           ├── 获取中立角度 _params.neutral_angles
│           ├── 设置目标类型为角度
│           └── 将角度从度转换为弧度并设置
│       ├── MAV_MOUNT_MODE_MAVLINK_TARGETING 模式
│           └── 处理传入消息时存储MAVLINK目标
│       ├── MAV_MOUNT_MODE_RC_TARGETING 模式
│           ├── 使用飞行员的RC输入更新目标
│           ├── 获取RC目标 get_rc_target()
│           └── 根据目标类型更新角度或速率
│       ├── MAV_MOUNT_MODE_GPS_POINT 模式
│           ├── 获取指向ROI的角度目标 get_angle_target_to_roi()
│           └── 设置目标类型为角度
│       ├── MAV_MOUNT_MODE_HOME_LOCATION 模式
│           ├── 获取指向Home位置的角度目标 get_angle_target_to_home()
│           └── 设置目标类型为角度
│       ├── MAV_MOUNT_MODE_SYSID_TARGET 模式
│           ├── 获取指向其他车辆的角度目标 get_angle_target_to_sysid()
│           └── 设置目标类型为角度
│       └── 其他模式
│           └── 引发内部错误 INTERNAL_ERROR()
├── 根据目标类型发送目标角度或速率
│   ├── 目标类型为角度
│       └── 发送目标角度 send_target_angles()
│   └── 目标类型为速率
│       └── 发送目标速率 send_target_rates()

3.3 AP_Mount_Siyi::read_incoming_packets

AP_Mount_Siyi::update时,调用该函数,对SIYI云台串口数据报文进行解析。

AP_Mount_Siyi::read_incoming_packets
|
|-- 检查串口接口上是否有数据可读
|   |-- 获取可读的字节数 `nbytes`
|   |-- 如果没有字节可读,则返回
|
|-- 初始化重置解析器状态的标志 `reset_parser`
|
|-- 遍历接收到的每个字节
|   |-- 读取一个字节 `b`
|   |-- 将字节添加到消息缓冲区 `_msg_buff`
|   |-- 检查消息是否过长,如果过长则重置解析器状态
|
|   |-- 根据当前解析状态处理字节
|       |-- `WAITING_FOR_HEADER_LOW`
|           |-- 如果字节为头部低字节 `AP_MOUNT_SIYI_HEADER1`,则转到 `WAITING_FOR_HEADER_HIGH` 状态
|           |-- 否则,重置解析器状态
|
|       |-- `WAITING_FOR_HEADER_HIGH`
|           |-- 如果字节为头部高字节 `AP_MOUNT_SIYI_HEADER2`,则转到 `WAITING_FOR_CTRL` 状态
|           |-- 否则,重置解析器状态
|
|       |-- `WAITING_FOR_CTRL`
|           |-- 转到 `WAITING_FOR_DATALEN_LOW` 状态
|
|       |-- `WAITING_FOR_DATALEN_LOW`
|           |-- 保存数据长度低字节,转到 `WAITING_FOR_DATALEN_HIGH` 状态
|
|       |-- `WAITING_FOR_DATALEN_HIGH`
|           |-- 保存数据长度高字节,并进行数据长度合理性检查
|           |-- 如果数据长度合理,转到 `WAITING_FOR_SEQ_LOW` 状态
|           |-- 否则,重置解析器状态并输出调试信息
|
|       |-- `WAITING_FOR_SEQ_LOW`
|           |-- 转到 `WAITING_FOR_SEQ_HIGH` 状态
|
|       |-- `WAITING_FOR_SEQ_HIGH`
|           |-- 转到 `WAITING_FOR_CMDID` 状态
|
|       |-- `WAITING_FOR_CMDID`
|           |-- 保存命令ID,如果有数据则转到 `WAITING_FOR_DATA` 状态,否则转到 `WAITING_FOR_CRC_LOW` 状态
|
|       |-- `WAITING_FOR_DATA`
|           |-- 接收数据字节,如果所有数据接收完毕,则转到 `WAITING_FOR_CRC_LOW` 状态
|
|       |-- `WAITING_FOR_CRC_LOW`
|           |-- 保存CRC低字节,转到 `WAITING_FOR_CRC_HIGH` 状态
|
|       |-- `WAITING_FOR_CRC_HIGH`
|           |-- 保存CRC高字节,进行CRC校验
|           |-- 如果CRC校验成功,则处理接收到的封包 `process_packet()`
|           |-- 否则,输出调试信息
|           |-- 重置解析器状态
|
|-- 重置解析器状态
|   |-- 重置解析状态为 `WAITING_FOR_HEADER_LOW`
|   |-- 清空消息缓冲区长度 `_msg_buff_len`
|   |-- 重置 `reset_parser` 标志

3.4 AP_Mount_Siyi::process_packet

云台摄像机报文处理,支持以下命令:

  • SiyiCommandId::ACQUIRE_FIRMWARE_VERSION(0x01)
  • SiyiCommandId::HARDWARE_ID(0x02)
  • SiyiCommandId::AUTO_FOCUS(0x04)
  • SiyiCommandId::MANUAL_ZOOM_AND_AUTO_FOCUS(0x05)
  • SiyiCommandId::MANUAL_FOCUS(0x06)
  • SiyiCommandId::GIMBAL_ROTATION(0x07)
  • SiyiCommandId::CENTER(0x08)
  • SiyiCommandId::ACQUIRE_GIMBAL_CONFIG_INFO(0x0A)
  • SiyiCommandId::FUNCTION_FEEDBACK_INFO(0x0B)
  • SiyiCommandId::PHOTO(0x0C)
  • SiyiCommandId::ACQUIRE_GIMBAL_ATTITUDE(0x0D)
  • SiyiCommandId::READ_RANGEFINDER(0x15)
AP_Mount_Siyi::process_packet
└── switch ((SiyiCommandId)_parsed_msg.command_id)
    ├── case SiyiCommandId::ACQUIRE_FIRMWARE_VERSION
    │   ├── 检查 _parsed_msg.data_bytes_received 是否为 128
    │   ├── 解析并显示固件版本
    │   ├── 检查摄像机固件版本是否全为零
    │   ├── 设置 _fw_version.received 为 true
    │   ├── 显示摄像机和云台固件版本
    │   ├── 显示变焦固件版本(如果存在)
    │   └── 检查固件版本是否为最新
    ├── case SiyiCommandId::HARDWARE_ID
    │   ├── 查找硬件 ID 的前两位
    │   ├── 设置 _hardware_model
    │   └── 设置 _got_hardware_id 为 true
    ├── case SiyiCommandId::AUTO_FOCUS
    │   └── #if AP_MOUNT_SIYI_DEBUG
    │       ├── 检查 _parsed_msg.data_bytes_received 是否为 1
    │       └── 调试输出自动对焦信息
    ├── case SiyiCommandId::MANUAL_ZOOM_AND_AUTO_FOCUS
    │   ├── 检查 _parsed_msg.data_bytes_received 是否为 2
    │   └── 解析变焦倍数并调试输出
    ├── case SiyiCommandId::MANUAL_FOCUS
    │   └── #if AP_MOUNT_SIYI_DEBUG
    │       ├── 检查 _parsed_msg.data_bytes_received 是否为 1
    │       └── 调试输出手动对焦信息
    ├── case SiyiCommandId::GIMBAL_ROTATION
    │   └── #if AP_MOUNT_SIYI_DEBUG
    │       ├── 检查 _parsed_msg.data_bytes_received 是否为 1
    │       └── 调试输出云台旋转信息
    ├── case SiyiCommandId::CENTER
    │   └── #if AP_MOUNT_SIYI_DEBUG
    │       ├── 检查 _parsed_msg.data_bytes_received 是否为 1
    │       └── 调试输出居中信息
    ├── case SiyiCommandId::ACQUIRE_GIMBAL_CONFIG_INFO
    │   ├── 保存之前的录制状态
    │   ├── 更新云台配置信息
    │   ├── 如果录制状态变化,向用户警报
    │   └── 调试输出云台配置信息
    ├── case SiyiCommandId::FUNCTION_FEEDBACK_INFO
    │   ├── 检查 _parsed_msg.data_bytes_received 是否为 1
    │   ├── 解析功能反馈信息
    │   └── 根据反馈信息进行相应操作
    ├── case SiyiCommandId::PHOTO
    │   └── 不执行任何操作
    ├── case SiyiCommandId::ACQUIRE_GIMBAL_ATTITUDE
    │   ├── 检查 _parsed_msg.data_bytes_received 是否为 12
    │   ├── 更新当前角度和速率
    │   └── 解析并转换角度和速率信息
    ├── case SiyiCommandId::READ_RANGEFINDER
    │   ├── 解析测距仪距离
    │   └── 更新最后一次测距时间
    └── default
        └── 调试输出未处理的命令 ID

#if AP_MOUNT_SIYI_DEBUG
└── 如果存在意外的数据长度
    └── 调试输出意外长度信息

3.5 AP_Mount_Siyi::update_zoom_control

对摄像头使用SiyiCommandId::MANUAL_ZOOM_AND_AUTO_FOCUS进行光学调焦。

AP_Mount_Siyi::update_zoom_control
└── if (_zoom_type == ZoomType::RATE)
    ├── const uint32_t now_ms = AP_HAL::millis();
    ├── if (now_ms - _last_zoom_control_ms < 1000)
    │   └── return;
    ├── _last_zoom_control_ms = now_ms;
    ├── if (!is_zero(_zoom_rate_target))
    │   └── send_zoom_rate(_zoom_rate_target);
    └── // 如果 _zoom_rate_target 是零,则不会发送缩放率,因为这会触发自动对焦

3.6 AP_Mount_Backend::set_rctargeting_on_rcinput_change

当RC控制配置辅助功能RC_Channel::AUX_FUNC::MOUNT1_ROLLRC_Channel::AUX_FUNC::MOUNT1_PITCHRC_Channel::AUX_FUNC::MOUNT1_YAW时,可以直接通过RC功能键进行手动控制。

AP_Mount_Backend::set_rctargeting_on_rcinput_change
├── 如果 (!rc().has_valid_input())
│   └── 返回;
├── roll_ch = rc().find_channel_for_option(...)
├── pitch_ch = rc().find_channel_for_option(...)
├── yaw_ch = rc().find_channel_for_option(...)
├── 如果 (!last_rc_input.initialised)
│   └── 初始化 last_rc_input
└── 如果 (get_mode() != MAV_MOUNT_MODE_RC_TARGETING && get_mode() != MAV_MOUNT_MODE_RETRACT)
    ├── roll_dz = MAX(roll_ch->get_dead_zone(), 10)
    ├── pitch_dz = MAX(pitch_ch->get_dead_zone(), 10)
    ├── yaw_dz = MAX(yaw_ch->get_dead_zone(), 10)
    └── 如果 (遥控器输入发生变化条件)
        └── 设置模式为 MAV_MOUNT_MODE_RC_TARGETING
└── 如果 (get_mode() == MAV_MOUNT_MODE_RC_TARGETING || get_mode() == MAV_MOUNT_MODE_RETRACT)
    └── 更新 last_rc_input

4. 辅助函数

4.1 基本能力

检查SIYI云台摄像头:

  1. 健康状态(无串口通信,认为设备不健康)
  2. 是否具备roll方向旋转能力
  3. 是否具备yaw方向旋转能力

注:ZT30具备3维光学吊舱;A2 mini仅具有Pitch。

    // return true if healthy
    bool healthy() const override;

    // return true if this mount accepts roll targets
    bool has_roll_control() const override { return false; }

    // has_pan_control - returns true if this mount can control its pan (required for multicopters)
    bool has_pan_control() const override { return yaw_range_valid(); };
    // returns true if user has configured a valid yaw angle range
    // allows user to disable yaw even on 3-axis gimbal
    bool yaw_range_valid() const { return (_params.yaw_angle_min < _params.yaw_angle_max); }

4.2 摄像头&红外测距功能

  • 摄像头
    //
    // camera controls
    //

    // take a picture.  returns true on success
    bool take_picture() override;

    // start or stop video recording
    // set start_recording = true to start record, false to stop recording
    bool record_video(bool start_recording) override;

    // set zoom specified as a rate or percentage
    bool set_zoom(ZoomType zoom_type, float zoom_value) override;

    // set focus specified as rate, percentage or auto
    // focus in = -1, focus hold = 0, focus out = 1
    SetFocusResult set_focus(FocusType focus_type, float focus_value) override;

    // set camera lens as a value from 0 to 8.  ZT30 only
    bool set_lens(uint8_t lens) override;

    // set_camera_source is functionally the same as set_lens except primary and secondary lenses are specified by type
    // primary and secondary sources use the AP_Camera::CameraSource enum cast to uint8_t
    bool set_camera_source(uint8_t primary_source, uint8_t secondary_source) override;

    // send camera information message to GCS
    void send_camera_information(mavlink_channel_t chan) const override;

    // send camera settings message to GCS
    void send_camera_settings(mavlink_channel_t chan) const override;
  • 红外测距
    //
    // rangefinder
    //

    // get rangefinder distance.  Returns true on success
    bool get_rangefinder_distance(float& distance_m) const override;

4.3 云台操作

  • 云台控制相关操作&功能
    // send packet to gimbal
    // returns true on success, false if outgoing serial buffer is full
    bool send_packet(SiyiCommandId cmd_id, const uint8_t* databuff, uint8_t databuff_len);
    bool send_1byte_packet(SiyiCommandId cmd_id, uint8_t data_byte);

    // request info from gimbal
    void request_firmware_version() { send_packet(SiyiCommandId::ACQUIRE_FIRMWARE_VERSION, nullptr, 0); }
    void request_hardware_id() { send_packet(SiyiCommandId::HARDWARE_ID, nullptr, 0); }
    void request_configuration() { send_packet(SiyiCommandId::ACQUIRE_GIMBAL_CONFIG_INFO, nullptr, 0); }
    void request_function_feedback_info() { send_packet(SiyiCommandId::FUNCTION_FEEDBACK_INFO, nullptr, 0); }
    void request_gimbal_attitude() { send_packet(SiyiCommandId::ACQUIRE_GIMBAL_ATTITUDE, nullptr, 0); }
    void request_rangefinder_distance() { send_packet(SiyiCommandId::READ_RANGEFINDER, nullptr, 0); }

    // rotate gimbal.  pitch_rate and yaw_rate are scalars in the range -100 ~ +100
    // yaw_is_ef should be true if gimbal should maintain an earth-frame target (aka lock)
    void rotate_gimbal(int8_t pitch_scalar, int8_t yaw_scalar, bool yaw_is_ef);

    // Set gimbal's motion mode if it has changed. Use force=true to always send.
    //   FOLLOW: roll and pitch are in earth-frame, yaw is in body-frame
    //   LOCK: roll, pitch and yaw are all in earth-frame
    //   FPV: roll, pitch and yaw are all in body-frame
    // Returns true if message successfully sent to Gimbal
    bool set_motion_mode(const GimbalMotionMode mode, const bool force=false);

    // send target pitch and yaw rates to gimbal
    // yaw_is_ef should be true if yaw_rads target is an earth frame rate, false if body_frame
    void send_target_rates(float pitch_rads, float yaw_rads, bool yaw_is_ef);

    // send target pitch and yaw angles to gimbal
    // yaw_is_ef should be true if yaw_rad target is an earth frame angle, false if body_frame
    void send_target_angles(float pitch_rad, float yaw_rad, bool yaw_is_ef);

    // send zoom rate command to camera. zoom out = -1, hold = 0, zoom in = 1
    bool send_zoom_rate(float zoom_value);

    // send zoom multiple command to camera. e.g. 1x, 10x, 30x
    bool send_zoom_mult(float zoom_mult);

    // get zoom multiple max
    float get_zoom_mult_max() const;

    // update zoom controller
    void update_zoom_control();

    // get model name string, returns nullptr if hardware id is unknown
    const char* get_model_name() const;

    // Checks that the firmware version on the Gimbal meets the minimum supported version.
    void check_firmware_version() const;

4. 总结

AP_Mount_Siyi是在云台基础上集成了摄像头、红外测距等诸多功能的复合设备。

  • 云台摄像头 串口接 飞控
  • AI模块 串口接 飞控

其中云台摄像头 串口接 飞控,走的就是本文所描述的协议。而AI模块 串口接飞控侧重的伴机电脑的目标跟踪。

在这里插入图片描述

5. 参考资料

【1】ArduPilot开源飞控系统之简单介绍
【2】ArduPilot之开源代码Task介绍
【3】ArduPilot飞控启动&运行过程简介
【4】ArduPilot之开源代码Library&Sketches设计
【5】ArduPilot之开源代码Sensor Drivers设计
【6】ArduPilot开源飞控之AP_Mount

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LabVIEW实现LED显示屏视觉检测

为了满足LED显示屏在生产过程中的严格质量检测需求&#xff0c;引入自动化检测系统是十分必要的。传统人工检测方式存在检测强度高、效率低、准确性差等问题&#xff0c;自动化检测系统则能显著提高检测效率和准确性。视觉检测系统的构建主要包含硬件和软件两个部分。 视觉系统…

深度学习论文: LLaMA: Open and Efficient Foundation Language Models

深度学习论文: LLaMA: Open and Efficient Foundation Language Models LLaMA: Open and Efficient Foundation Language Models PDF:https://arxiv.org/pdf/2302.13971.pdf PyTorch: https://github.com/shanglianlm0525/PyTorch-Networks 1 概述 本文介绍了LLaMA&#xff0…

一二三应用开发平台应用开发示例(7)——文档功能实现示例

概述 在完成文件夹配置工作后&#xff0c;接下来配置文档管理系统最核心的管理对象“文档”。 依旧是使用平台低代码配置工作来配置&#xff0c;配置流程跟文件夹的配置是相同的&#xff0c;以下简要说明&#xff0c;重点是新涉及到的功能或注意点。 创建实体 配置模型属性 …

【Dison夏令营 Day 15】 Python 鸡蛋捕手

在本次课程中&#xff0c;我们使用 Python 创建了经典的 "抓蛋 "游戏。在这个游戏中&#xff0c;每抓到一个鸡蛋就能赢得 10 分&#xff0c;而每掉落一个鸡蛋就会损失一条命。 小时候&#xff0c;我们都玩过 "抓鸡蛋 "游戏。我们使用海龟软件包在 Python …

【linux】阿里云centos配置邮件服务

目录 1.安装mailx服务 2./etc/mail.rc 配置增加 3.QQ邮箱开启smtp服务&#xff0c;获取授权码 4.端口设置&#xff1a;Linux 防火墙开放端口-CSDN博客 5.测试 1.安装mailx服务 yum -y install mailx 2./etc/mail.rc 配置增加 #邮件发送人 set from924066173qq.com #阿里…

windows USB 设备驱动开发-USB电源管理(一)

符合通用串行总线 (USB) 规范的 USB 设备的电源管理功能具有一组丰富而复杂的电源管理功能。 请务必了解这些功能如何与 Windows 驱动程序模型 (WDM) 交互&#xff0c;特别是 Microsoft Windows 如何调整标准 USB 功能以支持系统唤醒体系结构。 基于内核模式驱动程序框架的 US…

android13 rom 开发总纲说明

1. 这里是文章总纲&#xff0c;可以在这里快速找到需要的文章。 2. 文章一般是基于标准的android13&#xff0c;有一些文章可能会涉及到具体平台&#xff0c;例如全志&#xff0c;瑞芯微等一些平台。 3.系统应用 3.1系统应用Launcher3桌面相关&#xff1a; 3.2系统应用设置S…

NLP入门——词袋语言模型的搭建、训练与预测

卷积语言模型实际上是取了句子最后ctx_len个词作为上下文输入模型来预测之后的分词。但更好的选择是我们做一个词袋&#xff0c;将所有分词装在词袋中作为上下文&#xff0c;这样预测的分词不只根据最后ctx_len个分词&#xff0c;而是整个词袋中的所有分词。 例如我们的序列是&…

绝对值不等式运用(C++)

货仓选址 用数学公式表达题意&#xff0c;假设有位置a1~an,假设选址在x位置处&#xff0c;则有&#xff1a; 如何让这个最小&#xff0c;我们把两个式子整合一下&#xff0c;利用绝对值不等式&#xff1a; 我们知道&#xff1a; 如下图所示&#xff1a;到A&#xff0c;B两点&…

【深度学习入门篇 ②】Pytorch完成线性回归!

&#x1f34a;嗨&#xff0c;大家好&#xff0c;我是小森( &#xfe61;ˆoˆ&#xfe61; )&#xff01; 易编橙终身成长社群创始团队嘉宾&#xff0c;橙似锦计划领衔成员、阿里云专家博主、腾讯云内容共创官、CSDN人工智能领域优质创作者 。 易编橙&#xff1a;一个帮助编程小…

简单状压dp(以力扣464为例)

目录 1.状态压缩dp是啥&#xff1f; 2.题目分析 3.解题思路 4.算法分析 5.代码分析 6.代码一览 7.结语 1.状态压缩dp是啥&#xff1f; 顾名思义&#xff0c;状态压缩dp就是将原本会超出内存限制的存储改用更加有效的存储方式。简而言之&#xff0c;就是压缩dp的空间。 …

昇思MindSpore学习笔记6-05计算机视觉--SSD目标检测

摘要&#xff1a; 记录MindSpore AI框架使用SSD目标检测算法对图像内容识别的过程、步骤和方法。包括环境准备、下载数据集、数据采样、数据集加载和预处理、构建模型、损失函数、模型训练、模型评估等。 一、概念 1.模型简介 SSD目标检测算法 Single Shot MultiBox Detecto…

WebRTC群发消息API接口选型指南!怎么用?

WebRTC群发消息API接口安全性如何&#xff1f;API接口怎么优化&#xff1f; WebRTC技术在现代实时通信中占据了重要地位。对于需要实现群发消息功能的应用程序来说&#xff0c;选择合适的WebRTC群发消息API接口是至关重要的。AokSend将详细介绍WebRTC群发消息API接口的选型指南…

大数据开发者如何快速熟悉新公司业务

作为一名大数据开发工程师,进入一家新公司后快速熟悉业务是至关重要的。 目录 1. 了解产品形态故事1:电商平台的数据分析故事2:金融科技的风控系统故事3:社交媒体的推荐算法 2. 了解业务流程故事1:物流配送系统的优化故事2:医疗保险的理赔流程故事3:银行的贷款审批流程 3. 走…

openfoam生成的非均匀固体Solid数据分析、VTK数据格式分析、以及paraview官方用户指导文档和使用方法

一、openfoam生成的非均匀固体Solid数据分析 对于Solid/dealii-output文件&#xff0c;固体的数据文件&#xff0c; # vtk DataFile Version 3.0 #This file was generated by the deal.II library on 2024/7/10 at 9:46:15 ASCII DATASET UNSTRUCTURED_GRIDPOINTS 108000 do…

【算法】【二分法】二分法详解

先给y总打一个广告。&#xff08;我这种废物收不到钱&#xff09; 本科时候就在打蓝桥杯玩玩算法&#xff0c;当时听朋友的一个刷题且涵盖教程的网站&#xff0c;ACWING。 www.acwing.com 里面好处是大部分基础算法都有&#xff0c;Y总的视频&#xff01; y总我的神&#xff01…

IDEA之Debug的使用

自定义功能图表 功能说明 光标回到Debug行 执行到光标所在行 Force Step into Trace Current Stream Chain Reset Frame 重置方法入栈

C++基础学习笔记

1.命名空间(namespace) 1.什么是命名空间&命名空间的作用 1.在C/C中&#xff0c;变量、函数、类都是大量存在的&#xff0c;这些变量等的名称将都存在于全局作用域中&#xff0c;就会导致很多的命名冲突等。使用命名空间的目的就是对标识符的名称进行本地化&#xff0c;以…

Nginx七层(应用层)反向代理:UWSGI代理uwsgi_pass篇

Nginx七层&#xff08;应用层&#xff09;反向代理 UWSGI代理uwsgi_pass篇 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this a…

JavaSE学习笔记第二弹——对象和多态(下)

今天我们继续复习与JavaSE相关的知识&#xff0c;使用的编译器仍然是IDEA2022&#xff0c;大家伙使用eclipse或其他编译环境是一样的&#xff0c;都可以。 目录 数组 定义 一维数组 ​编辑 二维数组 多维数组 数组的遍历 for循环遍历 ​编辑 foreach遍历 封装、继承和…