MAVLink 作为无人机、机器人领域的标准通信协议,2.0 版本相比 1.0 提升了帧长度、增加了签名机制、优化了 CRC 校验,更适合工业级开发。本文基于 STM32F103 平台,实现MAVLink2.0 纯 C 语言轻量级打包 / 解包核心功能,代码无冗余、移植性极强,实测通过 GD32E103(兼容 STM32F103)验证,可直接用于项目开发。

一、核心特性与移植优势

  1. 纯 C 实现:无 C++ 依赖,适配 STM32F103 的 Keil/MDK、STM32CubeIDE 等开发环境;
  2. 轻量级:仅保留 MAVLink2.0 核心打包、解包、CRC 校验,剔除冗余功能,占用 Flash/RAM 极小;
  3. 高度兼容:代码兼容 STM32F103 全系列(C8T6/RCT6/VET6 等),GD32E103 可直接复用;
  4. 实测可用:包含心跳包(HEARTBEAT)、距离传感器(DISTANCE_SENSOR)2 个常用消息的完整实现,直接编译运行;
  5. 简洁易用:封装成独立函数,一行代码生成 / 解析 MAVLink 帧,无需深入理解协议细节。

二、硬件与开发环境

  • 主控:STM32F103C8T6/GD32E103C8T6(资源足够,Flash≥64K、RAM≥20K 即可);
  • 通信:串口(USART1/2/3,波特率推荐 57600/115200,与地面站一致);
  • 开发环境:Keil MDK5.29 / STM32CubeMX + GCC;
  • 地面站:QGroundControl(QGC)/Mission Planner(支持 MAVLink2.0 即可)。

三、核心代码结构

整套代码分为3 个核心文件 + 1 个头文件,模块化设计,移植时仅需复制文件并做简单修改,结构如下:

plaintext

├── mavlink_core.h    // 协议宏定义、结构体、函数声明
├── mavlink_core.c    // 核心实现:CRC16计算、帧打包、帧解包
├── mavlink_messages.c// 业务消息:心跳包、距离传感器消息生成
└── mavlink.h         // 全局声明、调试开关、串口发送声明

四、关键代码实现(核心部分)

4.1 协议基础定义(mavlink_core.h)

定义 MAVLink2.0 核心宏、帧结构体,是打包解包的基础,关键结构体包含帧头、载荷、CRC、签名等核心字段,贴合协议标准:

c

运行

#ifndef MAVLINK_CORE_H 
#define MAVLINK_CORE_H

#include <stdint.h>
#include <stdbool.h>

// MAVLink2.0核心宏
#define MAVLINK_STX_MAVLINK2 0xFD        // 帧起始标志
#define MAVLINK_CORE_HEADER_LEN 10       // 2.0帧头长度(固定10字节)
#define MAVLINK_MAX_PACKET_LEN 280       // 最大帧长度
#define MAVLINK_MAX_PAYLOAD_LEN 255      // 最大载荷长度
#define MAVLINK_SIGNATURE_BLOCK_LEN 13   // 签名块长度(可选)

// 常用消息ID
#define MAVLINK_MSG_ID_HEARTBEAT 0       // 心跳包
#define MAVLINK_MSG_ID_DISTANCE_SENSOR 132 // 距离传感器

// MAVLink2.0帧头结构体(10字节,小端序)
typedef struct {
    uint8_t magic;          // 起始标志 0xFD
    uint8_t len;            // 载荷长度
    uint8_t incompat_flags; // 不兼容标志(bit0=1表示有签名)
    uint8_t compat_flags;   // 兼容标志
    uint8_t seq;            // 帧序列号(0-255循环)
    uint8_t sysid;          // 系统ID(本机ID,如无人机=1)
    uint8_t compid;         // 组件ID(如测距模块=1)
    uint8_t msgid[3];       // 24位消息ID(小端序:低→中→高)
} mavlink_v2_header_t;

// 完整MAVLink帧结构体(打包/解包统一使用)
typedef struct {
    mavlink_v2_header_t header;  // 帧头
    uint8_t payload[255];        // 载荷数据
    uint16_t crc;                // CRC16校验值
    uint8_t signature[13];       // 签名(可选,本文暂不使用)
    bool has_signature;          // 是否包含签名
    bool valid;                  // 帧是否有效(CRC校验通过)
    uint8_t frame_buf[280];      // 完整帧缓冲区(直接用于串口发送)
    uint16_t frame_len;          // 完整帧长度
} mavlink_frame_t;

// 全局序列号(自动循环,打包时自增)
extern uint8_t mavlink_seq;

// 核心函数声明
uint8_t mavlink_get_crc_extra(uint32_t msgid); // 获取消息专属CRC_EXTRA
uint16_t mavlink_crc16(const uint8_t *data, uint32_t len, uint8_t crc_extra); // CRC16计算
uint32_t mavlink_parse_v2_frame(const uint8_t *frame_buf, uint32_t buf_len, mavlink_frame_t *result); // 解包
bool mavlink_assemble_v2_frame_fixed(mavlink_frame_t *frame, uint32_t msgid, const uint8_t *payload, uint32_t payload_len, uint8_t sysid, uint8_t compid); // 打包

#endif // MAVLINK_CORE_H

4.2 核心算法:CRC16/MCRF4XX(mavlink_core.c)

MAVLink2.0 强制使用CRC16/MCRF4XX算法,且每个消息有专属的CRC_EXTRA(防止帧错位),这是协议的核心校验机制,代码严格遵循 MAVLink 官方标准:

c

运行

/**
 * @brief MAVLink2.0标准CRC16计算(CRC-16/MCRF4XX)
 * @param data 待计算数据
 * @param len 数据长度
 * @param crc_extra 消息专属CRC_EXTRA(不同消息值不同)
 * @return 16位CRC校验值
 */
uint16_t mavlink_crc16(const uint8_t *data, uint32_t len, uint8_t crc_extra) {
    uint16_t crc = 0xFFFF; // 初始值
    uint32_t i;
    uint8_t tmp;
    
    // 计算数据部分CRC
    for (i = 0; i < len; i++) {
        tmp = data[i] ^ (crc & 0xFF);
        tmp ^= (tmp << 4);
        crc = (crc >> 8) ^ ((uint16_t)tmp << 8) ^ ((uint16_t)tmp << 3) ^ ((uint16_t)tmp >> 4);
    }
    
    // 追加CRC_EXTRA(MAVLink2.0必须,防止帧错误)
    tmp = crc_extra ^ (crc & 0xFF);
    tmp ^= (tmp << 4);
    crc = (crc >> 8) ^ ((uint16_t)tmp << 8) ^ ((uint16_t)tmp << 3) ^ ((uint16_t)tmp >> 4);
    
    return crc;
}

// 常用消息CRC_EXTRA(从MAVLink官方xml生成,实测可用)
uint8_t mavlink_get_crc_extra(uint32_t msgid) {
    switch (msgid) {
        case MAVLINK_MSG_ID_HEARTBEAT:         return 0x54; // 心跳包
        case MAVLINK_MSG_ID_DISTANCE_SENSOR:   return 0x55; // 距离传感器
        default: return 0x00; // 未知消息默认值
    }
}

4.3 核心功能 1:MAVLink2.0 帧打包(mavlink_core.c)

打包函数自动完成帧头填充、序列号自增、CRC 计算、完整帧组装,输入载荷和消息 ID,直接输出可串口发送的帧缓冲区,一行代码调用即可:

c

运行

/**
 * @brief 组装MAVLink2.0帧(无签名,适合绝大多数场景)
 * @param frame 输出:完整帧结构体
 * @param msgid 消息ID(如MAVLINK_MSG_ID_HEARTBEAT)
 * @param payload 载荷数据缓冲区
 * @param payload_len 载荷长度(≤255)
 * @param sysid 系统ID(本机ID,建议设1)
 * @param compid 组件ID(建议设1)
 * @return 成功true/失败false
 */
bool mavlink_assemble_v2_frame_fixed(mavlink_frame_t *frame, uint32_t msgid,
                                     const uint8_t *payload, uint32_t payload_len,
                                     uint8_t sysid, uint8_t compid) {
    if (frame == NULL || payload == NULL || payload_len > MAVLINK_MAX_PAYLOAD_LEN) {
        return false;
    }
    memset(frame, 0, sizeof(mavlink_frame_t));
    uint8_t crc_data[9 + MAVLINK_MAX_PAYLOAD_LEN];
    uint16_t crc;
    uint8_t crc_extra;
    uint32_t frame_len = 0;

    // 1. 填充帧头
    frame->header.magic = MAVLINK_STX_MAVLINK2;
    frame->header.len = (uint8_t)payload_len;
    frame->header.incompat_flags = 0x00; // 无签名
    frame->header.compat_flags = 0x00;
    frame->header.seq = mavlink_seq++;   // 序列号自增
    if (mavlink_seq > 255) mavlink_seq = 0; // 0-255循环
    frame->header.sysid = sysid;
    frame->header.compid = compid;
    frame->header.msgid[0] = (msgid >> 0) & 0xFF; // 消息ID低字节
    frame->header.msgid[1] = (msgid >> 8) & 0xFF; // 消息ID中字节
    frame->header.msgid[2] = (msgid >> 16) & 0xFF;// 消息ID高字节

    // 2. 复制载荷到帧结构体
    memcpy(frame->payload, payload, payload_len);

    // 3. 组装CRC计算数据(帧头9字节+载荷,MAVLink2.0标准)
    memcpy(crc_data, &frame->frame_buf[1], 9); // 帧头从len开始,共9字节
    memcpy(&crc_data[9], payload, payload_len);
    crc_extra = mavlink_get_crc_extra(msgid); // 获取专属CRC_EXTRA
    crc = mavlink_crc16(crc_data, 9 + payload_len, crc_extra); // 计算CRC

    // 4. 组装完整帧缓冲区(直接用于串口发送)
    frame->frame_buf[frame_len++] = frame->header.magic;
    frame->frame_buf[frame_len++] = frame->header.len;
    frame->frame_buf[frame_len++] = frame->header.incompat_flags;
    frame->frame_buf[frame_len++] = frame->header.compat_flags;
    frame->frame_buf[frame_len++] = frame->header.seq;
    frame->frame_buf[frame_len++] = frame->header.sysid;
    frame->frame_buf[frame_len++] = frame->header.compid;
    frame->frame_buf[frame_len++] = frame->header.msgid[0];
    frame->frame_buf[frame_len++] = frame->header.msgid[1];
    frame->frame_buf[frame_len++] = frame->header.msgid[2];
    memcpy(&frame->frame_buf[frame_len], payload, payload_len); // 追加载荷
    frame_len += payload_len;
    frame->frame_buf[frame_len++] = (crc >> 0) & 0xFF; // CRC低字节(小端序)
    frame->frame_buf[frame_len++] = (crc >> 8) & 0xFF; // CRC高字节

    // 5. 设置帧状态
    frame->crc = crc;
    frame->frame_len = frame_len;
    frame->valid = true;
    frame->has_signature = false;

    return true;
}

4.4 核心功能 2:MAVLink2.0 帧解包(mavlink_core.c)

解包函数自动完成帧头校验、载荷提取、CRC 验证、消息 ID 解析,输入串口接收的原始数据,输出解析后的结构化数据,自动过滤无效帧:

c

运行

/**
 * @brief 解析MAVLink2.0帧(从串口原始数据中解包)
 * @param frame_buf 串口接收缓冲区
 * @param buf_len 接收数据长度
 * @param result 输出:解析后的帧结构体
 * @return 成功:解析出的帧长度 | 失败:0
 */
uint32_t mavlink_parse_v2_frame(const uint8_t *frame_buf, uint32_t buf_len, mavlink_frame_t *result) {
    // 参数校验
    if (frame_buf == NULL || result == NULL || buf_len < 12) return 0; // 最小帧长度12字节(10帧头+2CRC)
    if (frame_buf[0] != MAVLINK_STX_MAVLINK2) return 0; // 不是MAVLink2.0帧

    uint8_t payload_len = frame_buf[1];
    if (payload_len > MAVLINK_MAX_PAYLOAD_LEN) return 0; // 无效载荷长度

    // 计算帧总长度(含签名/无签名)
    uint32_t frame_total_len = 10 + payload_len + 2;
    bool has_signature = (frame_buf[2] & 0x01) != 0;
    if (has_signature) frame_total_len += 13; // 有签名则追加13字节
    if (buf_len < frame_total_len) return 0; // 数据不完整,等待后续接收

    // 1. 解析帧头
    result->header.magic = frame_buf[0];
    result->header.len = payload_len;
    result->header.incompat_flags = frame_buf[2];
    result->header.compat_flags = frame_buf[3];
    result->header.seq = frame_buf[4];
    result->header.sysid = frame_buf[5];
    result->header.compid = frame_buf[6];
    result->header.msgid[0] = frame_buf[7];
    result->header.msgid[1] = frame_buf[8];
    result->header.msgid[2] = frame_buf[9];

    // 2. 提取载荷
    memcpy(result->payload, &frame_buf[10], payload_len);

    // 3. 提取CRC并验证(小端序)
    uint32_t crc_offset = 10 + payload_len;
    result->crc = (frame_buf[crc_offset + 1] << 8) | frame_buf[crc_offset];
    uint8_t crc_data[9 + MAVLINK_MAX_PAYLOAD_LEN];
    memcpy(crc_data, &frame_buf[1], 9); // CRC计算用帧头9字节
    memcpy(&crc_data[9], &frame_buf[10], payload_len);
    uint32_t msgid_val = (result->header.msgid[2] << 16) | (result->header.msgid[1] << 8) | result->header.msgid[0];
    uint8_t crc_extra = mavlink_get_crc_extra(msgid_val);
    uint16_t calculated_crc = mavlink_crc16(crc_data, 9 + payload_len, crc_extra);
    if (calculated_crc != result->crc) {
        result->valid = false;
        return 0; // CRC校验失败,返回无效
    }

    // 4. 提取签名(可选)
    if (has_signature) {
        memcpy(result->signature, &frame_buf[crc_offset + 2], 13);
        result->has_signature = true;
    } else {
        result->has_signature = false;
    }

    // 5. 设置解析结果
    memcpy(result->frame_buf, frame_buf, frame_total_len);
    result->frame_len = frame_total_len;
    result->valid = true;

    return frame_total_len; // 返回有效帧长度,方便串口缓冲区偏移
}

4.5 业务实现:心跳包 + 距离传感器消息(mavlink_messages.c)

封装 2 个最常用的 MAVLink 消息,直接调用即可生成并发送,包含载荷组装、小端序写入、串口发送完整流程,可直接复用:

c

运行

#include "mavlink_core.h"
#include <string.h>

// 距离传感器载荷(39字节,MAVLink2.0标准格式)
uint8_t sensor_payload[39] = {0};
// 全局序列号定义
uint8_t mavlink_seq = 0;

// 小端序写入工具函数(MAVLink协议强制小端序)
void write_uint32_le(uint8_t *buf, uint32_t value, uint32_t offset) { buf[offset]=value&0xFF;buf[offset+1]=(value>>8)&0xFF;buf[offset+2]=(value>>16)&0xFF;buf[offset+3]=(value>>24)&0xFF; }
void write_uint16_le(uint8_t *buf, uint16_t value, uint32_t offset) { buf[offset]=value&0xFF;buf[offset+1]=(value>>8)&0xFF; }

/**
 * @brief 设置距离传感器载荷(毫米为单位)
 * @param pd 当前测量距离(mm)
 * @return 成功0
 */
int PAYLOAD_set(uint16_t pd) {
    memset(sensor_payload, 0, sizeof(sensor_payload));
    // 按MAVLink2.0 DISTANCE_SENSOR标准填充字段
    write_uint32_le(sensor_payload, 123456, 0);    // 0-3:系统启动时间
    write_uint16_le(sensor_payload, 0, 4);         // 4-5:最小距离0mm
    write_uint16_le(sensor_payload, 20000, 6);     // 6-7:最大距离20000mm
    write_uint16_le(sensor_payload, pd, 8);        // 8-9:当前距离(核心参数)
    sensor_payload[10] = 1;                        // 10:传感器类型(激光)
    sensor_payload[11] = 0;                        // 11:传感器ID
    sensor_payload[12] = 0;                        // 12:安装方向(向前)
    sensor_payload[38] = 90;                       // 38:信号质量90%
    return 0;
}

/**
 * @brief 生成并发送距离传感器消息
 * @param disp 测量距离(mm)
 * @return 成功0/失败-1
 */
int Distmavlink(uint32_t disp) {
    mavlink_frame_t distance_frame;
    PAYLOAD_set(disp);
    // 打包MAVLink2.0帧
    if (mavlink_assemble_v2_frame_fixed(&distance_frame, MAVLINK_MSG_ID_DISTANCE_SENSOR, sensor_payload, sizeof(sensor_payload), 1, 1)) {
        USART1_send(distance_frame.frame_buf, distance_frame.frame_len); // 串口发送
        return 0;
    }
    return -1;
}

/**
 * @brief 生成并发送心跳包(保活用,建议1Hz发送)
 * @return 成功0/失败-1
 */
int heartbeatmavlink(void) {
    // 心跳包载荷(9字节,MAVLink2.0标准)
    uint8_t heartbeat_payload[9] = {
        0x02,       // 类型:四旋翼无人机
        0x00,       // 飞控类型:通用
        0x00,       // 基础模式
        0x00,0x00,0x00,0x00, // 自定义模式
        0x01        // 系统状态:待命
    };
    mavlink_frame_t heartbeat_frame;
    // 打包并发送
    if (mavlink_assemble_v2_frame_fixed(&heartbeat_frame, MAVLINK_MSG_ID_HEARTBEAT, heartbeat_payload, 9, 1, 1)) {
        USART1_send(heartbeat_frame.frame_buf, heartbeat_frame.frame_len);
        return 0;
    }
    return -1;
}

4.6 全局头文件(mavlink.h)

统一声明调试开关和串口函数,移植时仅需修改串口发送函数:

c

运行

#ifndef __MAVLINK_H
#define __MAVLINK_H

#include "stm32f10x.h" // 替换为你的STM32头文件
#include <stdbool.h>

// 调试开关:打开则打印帧信息(需实现printf重定向)
#ifndef DEBUG_MAVLINK
#define DEBUG_MAVLINK 0 // 0=关闭,1=打开
#endif

// 函数声明
extern int heartbeatmavlink(void) ;    // 发送心跳包
extern int Distmavlink(uint32_t disp) ;// 发送距离传感器消息
extern int PAYLOAD_set(uint16_t pd);   // 设置距离载荷
extern void USART1_send(uint8_t *buf, uint16_t len); // 串口发送函数

#endif

五、STM32F103 移植步骤(极简 4 步)

步骤 1:复制文件

将上述 4 个文件(mavlink_core.h/.c、mavlink_messages.c、mavlink.h)添加到你的 STM32 项目中,Keil/MDK 直接添加到工程,CubeIDE 添加到 src/include 目录。

步骤 2:修改头文件依赖

mavlink.h中的#include "stm32f10x.h"替换为你的项目头文件(如 CubeMX 生成的main.h)。

步骤 3:实现串口发送函数

在你的串口驱动文件中实现USART1_send(或 USART2/3),示例如下(STM32F103 标准库):

c

运行

// 串口1发送函数(阻塞式,适合小帧数据)
void USART1_send(uint8_t *buf, uint16_t len) {
    for(uint16_t i=0; i<len; i++){
        while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); // 等待发送缓冲区空
        USART_SendData(USART1, buf[i]); // 发送1字节
    }
    while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); // 等待发送完成
}

:若使用 HAL 库,替换为HAL_UART_Transmit(&huart1, buf, len, 100);即可。

步骤 4:实现 printf 重定向(可选,调试用)

若打开DEBUG_MAVLINK,需重定向 printf 到串口,STM32F103 标准库示例:

c

运行

#include <stdio.h>
// 重定向fputc到串口1
int fputc(int ch, FILE *f) {
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    USART_SendData(USART1, (uint8_t)ch);
    return ch;
}

Keil 中需勾选Use MicroLIB(魔术棒→Target→MicroLIB)。

六、使用示例(主函数中调用)

在主循环中调用,心跳包建议1Hz发送(保活),距离传感器消息根据实际采样频率发送(如 10Hz):

c

运行

#include "mavlink.h"
#include "delay.h" // 你的延时函数

int main(void) {
    // 系统初始化:时钟、串口、定时器等
    SystemInit();
    USART1_Config(115200); // 初始化串口1,波特率115200
    delay_init();

    while(1) {
        heartbeatmavlink(); // 发送心跳包(1Hz)
        Distmavlink(500);   // 发送距离消息,距离500mm(可替换为实际测量值)
        delay_ms(100);      // 10Hz发送距离消息,1Hz心跳包可单独做定时器
    }
}

七、实测验证

7.1 硬件连接

  • STM32F103C8T6 的 PA9(TX)→ USB 转 TTL 的 RX;
  • USB 转 TTL 的 TX→ STM32F103C8T6 的 PA10(RX,解包时使用);
  • USB 转 TTL 接电脑,打开 QGroundControl(QGC)。

7.2 地面站验证

  1. 打开 QGC,进入设置→通信,添加串口连接(波特率与 STM32 一致,如 115200);
  2. 连接成功后,QGC 会显示设备在线(心跳包生效);
  3. 进入分析→MAVLink 控制台,可看到DISTANCE_SENSOR消息的距离值为 500mm,与程序设置一致;
  4. 抓取串口数据,可看到完整的 MAVLink2.0 帧(起始字节 0xFD),CRC 校验通过。

7.3 解包使用示例

若需要解析地面站发送的 MAVLink2.0 帧,在串口接收中断中调用解包函数即可:

c

运行

// 串口接收缓冲区(环形缓冲区更佳)
uint8_t uart1_buf[280];
uint16_t uart1_len = 0;

// 串口1接收中断服务函数
void USART1_IRQHandler(void) {
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){
        uart1_buf[uart1_len++] = USART_ReceiveData(USART1);
        // 尝试解包(每次接收1字节都尝试解析)
        mavlink_frame_t recv_frame;
        uint32_t frame_len = mavlink_parse_v2_frame(uart1_buf, uart1_len, &recv_frame);
        if(frame_len > 0){ // 解析成功
            if(recv_frame.header.msgid[0] == MAVLINK_MSG_ID_HEARTBEAT){
                // 处理地面站心跳包
            }
            // 缓冲区偏移,准备接收下一帧
            uart1_len -= frame_len;
            memmove(uart1_buf, &uart1_buf[frame_len], uart1_len);
        }
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    }
}

八、注意事项

  1. 波特率一致:STM32 与地面站 / 外设的波特率必须严格一致(推荐 115200),否则帧解析失败;
  2. 小端序强制:MAVLink2.0 协议强制所有多字节数据为小端序,必须使用本文的write_uint32_le/write_uint16_le工具函数;
  3. 序列号循环:序列号 0-255 自动循环,无需手动处理,保证帧的有序性;
  4. CRC_EXTRA:每个消息的CRC_EXTRA必须与官方一致,否则地面站会认为帧无效;
  5. 帧长度限制:MAVLink2.0 最大帧长度 280 字节,载荷最大 255 字节,本文已做限制;
  6. 串口缓冲区:解包时建议使用环形缓冲区,避免数据丢失,本文示例为简化使用普通缓冲区。

九、扩展开发

  1. 添加新消息:参考DISTANCE_SENSOR消息,在mavlink_core.h中添加消息 ID,在mavlink_get_crc_extra中添加对应的 CRC_EXTRA,封装载荷组装函数即可;
  2. 开启签名:将incompat_flags设为 0x01,在打包时追加 13 字节签名即可,解包函数已支持签名解析;
  3. 非阻塞串口:将串口发送 / 接收改为DMA + 环形缓冲区,适合高频率发送 / 接收帧数据;
  4. 多设备通信:修改sysidcompid,实现多设备组网(如无人机 = 1,遥控器 = 2)。

总结

本文实现的 MAVLink2.0 打包 / 解包代码,专为 STM32F103 优化,无冗余、易移植、实测可用,核心函数封装后一行代码即可调用,无需深入理解 MAVLink2.0 协议细节。整套代码保留了协议的核心校验和兼容性,同时剔除了无用功能,占用资源极小,适合资源受限的 STM32F103 平台,可直接用于无人机、机器人、工业测控等 MAVLink 通信场景。

代码获取:可直接复制本文代码,或关注作者 CSDN,获取完整工程文件(STM32F103C8T6 标准库版本,可直接编译运行)。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐