当前位置: 首页 > news >正文

DVL多普勒速度计原理与嵌入式实现

前言

最近在做水下机器人项目,需要用到DVL(Doppler Velocity Log)进行速度测量和定位。市面上的DVL产品动辄几万甚至十几万,对于小型项目来说成本确实有点高。所以花了些时间研究了DVL的工作原理,并尝试用STM32实现了一个简化版本的DVL数据处理系统。这篇文章主要分享DVL的测速原理和具体实现代码,希望对做水下机器人或者导航系统的朋友有所帮助。

一、什么是DVL

DVL是Doppler Velocity Log的缩写,中文叫多普勒速度计或者声学多普勒测速仪。它主要用在水下航行器上,通过发射声波并接收反射回来的声波,利用多普勒效应计算出载体相对于海底或水体的运动速度。

DVL在水下定位导航中的地位类似于陆地上的轮式里程计,但精度要高得多。商用DVL的速度测量精度可以达到0.2%,这个精度对于水下长航时导航来说已经相当不错了。

##二、多普勒效应基础

2.1 什么是多普勒效应

多普勒效应大家应该都有生活经验:当救护车向你驶来时,听到的警报声音调会变高;远离你时音调会变低。这就是多普勒效应。

声波也好,电磁波也好,当波源和观察者之间有相对运动时,观察者接收到的频率会发生变化。如果相互靠近,频率升高;相互远离,频率降低。

2.2 多普勒频移公式

对于声波在水中的传播,多普勒频移的基本公式是:

fd = 2 * f0 * v * cos(θ) / c

其中:

  • fd:多普勒频移(接收频率与发射频率之差)
  • f0:发射频率
  • v:载体速度
  • θ:波束与运动方向的夹角
  • c:声速(水中约1500m/s)

这里的系数2是因为声波经历了两次多普勒效应:一次是载体发射声波时,一次是反射回来接收时。

反过来,如果我们测量到了频移fd,就可以计算出速度的分量:

v = fd * c / (2 * f0 * cos(θ))

三、DVL的工作原理

3.1 四波束配置

标准的DVL采用四波束Janus配置(也叫贾纳斯配置)。四个换能器呈十字形布置,每个波束与垂直方向成一定角度,通常是25°到30°。

        前↑换能器1|
左 ←─ 载体 ─→ 右|换能器3↓后

侧视图看:

      |/|\/ | \1  |  2

3.2 速度解算

四个波束分别测得速度分量v1、v2、v3、v4后,可以解算出载体在三个方向的速度:

Vx = (v1 - v3) / (2 * sin(θ))
Vy = (v2 - v4) / (2 * sin(θ))
Vz = (v1 + v2 + v3 + v4) / (4 * cos(θ))

其中θ是波束与垂直方向的夹角。这样的配置有个好处:四个波束互为冗余,可以通过误差检测剔除坏数据。

3.3 两种工作模式

DVL有两种工作模式:

1. 底跟踪模式(Bottom Track)
声波打到海底后反射回来,测量的是相对海底的速度。这种模式精度最高,但要求水深在一定范围内(通常600米以内)。

2. 水跟踪模式(Water Track)
声波被水中的悬浮颗粒反射,测量的是相对水体的速度。深海或者离底太高时使用这种模式。

四、信号处理流程

DVL的信号处理主要包括以下几个步骤:

4.1 发射声脉冲

发射频率通常在几百kHz到几MHz之间。水下用的一般是300kHz~600kHz,近距离高精度的可能用到1.2MHz。脉冲宽度一般在几十到几百微秒。

4.2 接收与相关运算

接收到的回波信号与发射信号做互相关或自相关运算,找到最强的相关峰,这个峰对应的延迟就是声波往返时间。

4.3 频率估计

通过FFT或者自相关法估计多普勒频移。常用的方法有:

  • FFT频谱分析
  • 相位差分法
  • 自相关法(本文代码采用这种)

4.4 速度计算与滤波

根据多普勒频移计算速度,然后进行卡尔曼滤波或移动平均滤波,减小噪声影响。

五、代码实现

下面是基于STM32的DVL数据处理代码实现。这里主要展示核心算法部分,包括信号处理、频率估计和速度解算。

5.1 基础数据结构定义

// dvl_core.h
#ifndef DVL_CORE_H
#define DVL_CORE_H#include <stdint.h>
#include <math.h>// DVL配置参数
#define DVL_NUM_BEAMS 4
#define DVL_SAMPLE_RATE 1000000  // 1MHz采样率
#define DVL_CARRIER_FREQ 600000  // 600kHz载波
#define DVL_SOUND_SPEED 1500.0f  // 声速m/s
#define DVL_BEAM_ANGLE 25.0f     // 波束角度
#define DVL_FFT_SIZE 1024
#define DVL_PING_INTERVAL 100    // 发射间隔ms// 波束数据结构
typedef struct {float velocity;           // 测得速度分量float correlation;        // 相关系数float snr;               // 信噪比uint8_t valid;           // 数据有效性float range;             // 距离
} DVL_BeamData_t;// DVL测量数据
typedef struct {DVL_BeamData_t beams[DVL_NUM_BEAMS];float vx;                // X方向速度float vy;                // Y方向速度float vz;                // Z方向速度float altitude;          // 离底高度uint32_t timestamp;      // 时间戳uint8_t mode;            // 工作模式:0-底跟踪 1-水跟踪
} DVL_Data_t;// DVL系统结构
typedef struct {DVL_Data_t current_data;float beam_angle_rad;float sin_beam_angle;float cos_beam_angle;uint32_t ping_counter;// 滤波器状态float vx_filtered;float vy_filtered;float vz_filtered;float alpha;  // 一阶低通滤波系数
} DVL_Handle_t;// 函数声明
void DVL_Init(DVL_Handle_t *hdvl);
void DVL_ProcessPing(DVL_Handle_t *hdvl, int16_t *raw_data[DVL_NUM_BEAMS], uint32_t samples);
float DVL_EstimateDopplerShift(int16_t *signal, uint32_t length);
void DVL_ComputeVelocity(DVL_Handle_t *hdvl);
float DVL_CalculateCorrelation(int16_t *sig1, int16_t *sig2, uint32_t length);
void DVL_ApplyFilter(DVL_Handle_t *hdvl);#endif // DVL_CORE_H

5.2 核心算法实现

// dvl_core.c
#include "dvl_core.h"
#include <string.h>
#include <stdlib.h>#define PI 3.14159265358979323846f// 初始化DVL
void DVL_Init(DVL_Handle_t *hdvl) {memset(hdvl, 0, sizeof(DVL_Handle_t));// 预计算三角函数hdvl->beam_angle_rad = DVL_BEAM_ANGLE * PI / 180.0f;hdvl->sin_beam_angle = sinf(hdvl->beam_angle_rad);hdvl->cos_beam_angle = cosf(hdvl->beam_angle_rad);// 滤波器系数(截止频率约5Hz)hdvl->alpha = 0.3f;hdvl->current_data.mode = 0; // 默认底跟踪
}// 处理一次Ping数据
void DVL_ProcessPing(DVL_Handle_t *hdvl, int16_t *raw_data[DVL_NUM_BEAMS], uint32_t samples) {hdvl->ping_counter++;// 处理每个波束for(int i = 0; i < DVL_NUM_BEAMS; i++) {// 估计多普勒频移float doppler_shift = DVL_EstimateDopplerShift(raw_data[i], samples);// 计算速度分量float velocity_component = doppler_shift * DVL_SOUND_SPEED / (2.0f * DVL_CARRIER_FREQ * hdvl->cos_beam_angle);hdvl->current_data.beams[i].velocity = velocity_component;// 计算信噪比(简化版)float signal_power = 0;float noise_power = 0;for(uint32_t j = 0; j < samples/2; j++) {signal_power += raw_data[i][j] * raw_data[i][j];}for(uint32_t j = samples/2; j < samples; j++) {noise_power += raw_data[i][j] * raw_data[i][j];}hdvl->current_data.beams[i].snr = 10.0f * log10f(signal_power / (noise_power + 1e-6f));hdvl->current_data.beams[i].valid = (hdvl->current_data.beams[i].snr > 10.0f) ? 1 : 0;}// 解算三维速度DVL_ComputeVelocity(hdvl);// 应用滤波DVL_ApplyFilter(hdvl);hdvl->current_data.timestamp = HAL_GetTick();
}// 估计多普勒频移(使用自相关方法)
float DVL_EstimateDopplerShift(int16_t *signal, uint32_t length) {// 自相关方法估计频率// 计算一个载波周期的延迟uint32_t lag = DVL_SAMPLE_RATE / DVL_CARRIER_FREQ;float sum_real = 0;float sum_imag = 0;float sum_power = 0;// 计算相位差for(uint32_t i = 0; i < length - lag; i++) {float s1 = signal[i];float s2 = signal[i + lag];sum_real += s1 * s2;sum_power += s1 * s1;}// 归一化float correlation = sum_real / (sum_power + 1e-6f);// 从相关系数估计频移// 相位差 = 2*pi*fd*Tfloat phase_diff = acosf(correlation);float doppler_shift = phase_diff * DVL_SAMPLE_RATE / (2.0f * PI * lag);return doppler_shift;
}// 计算三维速度
void DVL_ComputeVelocity(DVL_Handle_t *hdvl) {DVL_BeamData_t *beams = hdvl->current_data.beams;// 检查数据有效性uint8_t valid_count = 0;for(int i = 0; i < DVL_NUM_BEAMS; i++) {if(beams[i].valid) valid_count++;}if(valid_count < 3) {// 有效波束数不足,无法解算return;}// 四波束Janus配置速度解算// 波束1: 前, 波束2: 右, 波束3: 后, 波束4: 左float v1 = beams[0].velocity;float v2 = beams[1].velocity;float v3 = beams[2].velocity;float v4 = beams[3].velocity;// X方向(前后)速度if(beams[0].valid && beams[2].valid) {hdvl->current_data.vx = (v1 - v3) / (2.0f * hdvl->sin_beam_angle);}// Y方向(左右)速度if(beams[1].valid && beams[3].valid) {hdvl->current_data.vy = (v2 - v4) / (2.0f * hdvl->sin_beam_angle);}// Z方向(垂直)速度if(valid_count == 4) {hdvl->current_data.vz = (v1 + v2 + v3 + v4) / (4.0f * hdvl->cos_beam_angle);}// 计算离底高度(基于飞行时间)// 这里需要从原始数据中提取,简化处理hdvl->current_data.altitude = 5.0f; // 示例值
}// 应用低通滤波
void DVL_ApplyFilter(DVL_Handle_t *hdvl) {float alpha = hdvl->alpha;hdvl->vx_filtered = alpha * hdvl->current_data.vx + (1 - alpha) * hdvl->vx_filtered;hdvl->vy_filtered = alpha * hdvl->current_data.vy + (1 - alpha) * hdvl->vy_filtered;hdvl->vz_filtered = alpha * hdvl->current_data.vz + (1 - alpha) * hdvl->vz_filtered;
}// 计算相关系数
float DVL_CalculateCorrelation(int16_t *sig1, int16_t *sig2, uint32_t length) {float sum_product = 0;float sum_sq1 = 0;float sum_sq2 = 0;for(uint32_t i = 0; i < length; i++) {sum_product += sig1[i] * sig2[i];sum_sq1 += sig1[i] * sig1[i];sum_sq2 += sig2[i] * sig2[i];}return sum_product / sqrtf(sum_sq1 * sum_sq2 + 1e-6f);
}

5.3 卡尔曼滤波器增强

为了进一步提高精度,可以加入卡尔曼滤波器:

// dvl_kalman.h
#ifndef DVL_KALMAN_H
#define DVL_KALMAN_H#include <stdint.h>// 卡尔曼滤波器状态
typedef struct {// 状态向量 [vx, vy, vz, ax, ay, az]float x[6];// 协方差矩阵(简化为对角阵)float P[6];// 过程噪声float Q[6];// 测量噪声float R[3];float dt;  // 时间步长
} DVL_Kalman_t;void DVL_Kalman_Init(DVL_Kalman_t *kf, float dt);
void DVL_Kalman_Predict(DVL_Kalman_t *kf);
void DVL_Kalman_Update(DVL_Kalman_t *kf, float vx, float vy, float vz);#endif
// dvl_kalman.c
#include "dvl_kalman.h"
#include <string.h>void DVL_Kalman_Init(DVL_Kalman_t *kf, float dt) {memset(kf, 0, sizeof(DVL_Kalman_t));kf->dt = dt;// 初始协方差for(int i = 0; i < 6; i++) {kf->P[i] = 1.0f;}// 过程噪声(根据实际情况调整)kf->Q[0] = 0.01f;  // vxkf->Q[1] = 0.01f;  // vykf->Q[2] = 0.01f;  // vzkf->Q[3] = 0.1f;   // axkf->Q[4] = 0.1f;   // aykf->Q[5] = 0.1f;   // az// 测量噪声kf->R[0] = 0.05f;  // vx测量噪声kf->R[1] = 0.05f;  // vy测量噪声kf->R[2] = 0.05f;  // vz测量噪声
}void DVL_Kalman_Predict(DVL_Kalman_t *kf) {float dt = kf->dt;// 状态预测:v = v + a*dtkf->x[0] += kf->x[3] * dt;  // vxkf->x[1] += kf->x[4] * dt;  // vykf->x[2] += kf->x[5] * dt;  // vz// 协方差预测for(int i = 0; i < 6; i++) {kf->P[i] += kf->Q[i];}
}void DVL_Kalman_Update(DVL_Kalman_t *kf, float vx, float vy, float vz) {// 卡尔曼增益float K[3];K[0] = kf->P[0] / (kf->P[0] + kf->R[0]);K[1] = kf->P[1] / (kf->P[1] + kf->R[1]);K[2] = kf->P[2] / (kf->P[2] + kf->R[2]);// 状态更新kf->x[0] += K[0] * (vx - kf->x[0]);kf->x[1] += K[1] * (vy - kf->x[1]);kf->x[2] += K[2] * (vz - kf->x[2]);// 协方差更新kf->P[0] *= (1.0f - K[0]);kf->P[1] *= (1.0f - K[1]);kf->P[2] *= (1.0f - K[2]);
}

5.4 串口通信接口

DVL通常通过串口输出数据,这里实现一个简单的协议:

// dvl_comm.h
#ifndef DVL_COMM_H
#define DVL_COMM_H#include "dvl_core.h"#define DVL_FRAME_HEADER 0xAA55
#define DVL_FRAME_MAX_SIZE 128// 数据帧结构
typedef struct __attribute__((packed)) {uint16_t header;uint8_t length;uint8_t msg_id;uint32_t timestamp;float vx;float vy;float vz;float altitude;uint8_t status;uint16_t checksum;
} DVL_DataFrame_t;void DVL_SendData(DVL_Handle_t *hdvl);
uint16_t DVL_CalculateChecksum(uint8_t *data, uint16_t length);#endif
// dvl_comm.c
#include "dvl_comm.h"
#include "usart.h"void DVL_SendData(DVL_Handle_t *hdvl) {DVL_DataFrame_t frame;frame.header = DVL_FRAME_HEADER;frame.length = sizeof(DVL_DataFrame_t) - 3;frame.msg_id = 0x01;frame.timestamp = hdvl->current_data.timestamp;frame.vx = hdvl->vx_filtered;frame.vy = hdvl->vy_filtered;frame.vz = hdvl->vz_filtered;frame.altitude = hdvl->current_data.altitude;frame.status = 0;// 计算校验和frame.checksum = DVL_CalculateChecksum((uint8_t*)&frame, sizeof(frame) - 2);// 发送数据HAL_UART_Transmit(&huart1, (uint8_t*)&frame, sizeof(frame), 100);
}uint16_t DVL_CalculateChecksum(uint8_t *data, uint16_t length) {uint16_t sum = 0;for(uint16_t i = 0; i < length; i++) {sum += data[i];}return sum;
}

5.5 主程序示例

// main.c
#include "dvl_core.h"
#include "dvl_kalman.h"
#include "dvl_comm.h"DVL_Handle_t hdvl;
DVL_Kalman_t kf;// 模拟ADC采样数据
int16_t adc_buffer[DVL_NUM_BEAMS][1024];void DVL_Task(void) {static uint32_t last_ping = 0;uint32_t now = HAL_GetTick();if(now - last_ping >= DVL_PING_INTERVAL) {last_ping = now;// 触发ADC采样(实际应用中通过DMA)// 这里用模拟数据for(int i = 0; i < DVL_NUM_BEAMS; i++) {// 生成模拟信号(实际中是ADC采样)for(int j = 0; j < 1024; j++) {// 载波 + 多普勒频移 + 噪声float t = (float)j / DVL_SAMPLE_RATE;float doppler = 100.0f * (i - 1.5f); // 模拟不同波束的频移float signal = 2000.0f * sinf(2.0f * PI * (DVL_CARRIER_FREQ + doppler) * t);adc_buffer[i][j] = (int16_t)(signal + (rand() % 200 - 100));}}// 处理数据int16_t *beam_data[DVL_NUM_BEAMS];for(int i = 0; i < DVL_NUM_BEAMS; i++) {beam_data[i] = adc_buffer[i];}DVL_ProcessPing(&hdvl, beam_data, 1024);// 卡尔曼滤波DVL_Kalman_Predict(&kf);DVL_Kalman_Update(&kf, hdvl.current_data.vx, hdvl.current_data.vy, hdvl.current_data.vz);// 发送数据DVL_SendData(&hdvl);// 输出调试信息printf("DVL: Vx=%.3f, Vy=%.3f, Vz=%.3f m/s\r\n", kf.x[0], kf.x[1], kf.x[2]);}
}int main(void) {HAL_Init();SystemClock_Config();// 初始化外设MX_GPIO_Init();MX_USART1_UART_Init();MX_ADC1_Init();// 初始化DVLDVL_Init(&hdvl);DVL_Kalman_Init(&kf, DVL_PING_INTERVAL / 1000.0f);printf("DVL System Initialized\r\n");while(1) {DVL_Task();HAL_Delay(1);}
}

六、Python数据分析工具

配套写了一个Python脚本用来接收和可视化DVL数据:

# dvl_monitor.py
import serial
import struct
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
from collections import dequeclass DVLMonitor:def __init__(self, port='COM3', baudrate=115200):self.ser = serial.Serial(port, baudrate, timeout=1)self.vx_data = deque(maxlen=200)self.vy_data = deque(maxlen=200)self.vz_data = deque(maxlen=200)self.time_data = deque(maxlen=200)self.start_time = 0# 设置图形self.fig, (self.ax1, self.ax2, self.ax3) = plt.subplots(3, 1, figsize=(10, 8))def parse_frame(self, data):"""解析DVL数据帧"""if len(data) < 26:return Noneheader = struct.unpack('<H', data[0:2])[0]if header != 0xAA55:return Nonelength = data[2]msg_id = data[3]timestamp = struct.unpack('<I', data[4:8])[0]vx = struct.unpack('<f', data[8:12])[0]vy = struct.unpack('<f', data[12:16])[0]vz = struct.unpack('<f', data[16:20])[0]altitude = struct.unpack('<f', data[20:24])[0]status = data[24]return {'timestamp': timestamp,'vx': vx,'vy': vy,'vz': vz,'altitude': altitude,'status': status}def update_plot(self, frame_data):"""更新实时曲线"""if self.start_time == 0:self.start_time = frame_data['timestamp']t = (frame_data['timestamp'] - self.start_time) / 1000.0self.time_data.append(t)self.vx_data.append(frame_data['vx'])self.vy_data.append(frame_data['vy'])self.vz_data.append(frame_data['vz'])# 清空并重绘self.ax1.clear()self.ax2.clear()self.ax3.clear()self.ax1.plot(self.time_data, self.vx_data, 'r-', label='Vx')self.ax1.set_ylabel('Vx (m/s)')self.ax1.legend()self.ax1.grid(True)self.ax2.plot(self.time_data, self.vy_data, 'g-', label='Vy')self.ax2.set_ylabel('Vy (m/s)')self.ax2.legend()self.ax2.grid(True)self.ax3.plot(self.time_data, self.vz_data, 'b-', label='Vz')self.ax3.set_ylabel('Vz (m/s)')self.ax3.set_xlabel('Time (s)')self.ax3.legend()self.ax3.grid(True)plt.tight_layout()def run(self):"""主循环"""buffer = b''def update(frame):nonlocal buffer# 读取串口数据if self.ser.in_waiting:buffer += self.ser.read(self.ser.in_waiting)# 查找帧头while len(buffer) >= 26:idx = buffer.find(b'\x55\xAA')if idx == -1:buffer = buffer[-1:]breakif idx > 0:buffer = buffer[idx:]# 解析数据帧frame_data = self.parse_frame(buffer[:26])if frame_data:self.update_plot(frame_data)print(f"T:{frame_data['timestamp']:.3f} "f"Vx:{frame_data['vx']:.3f} "f"Vy:{frame_data['vy']:.3f} "f"Vz:{frame_data['vz']:.3f}")buffer = buffer[26:]ani = FuncAnimation(self.fig, update, interval=50, cache_frame_data=False)plt.show()def __del__(self):if hasattr(self, 'ser') and self.ser.is_open:self.ser.close()if __name__ == '__main__':monitor = DVLMonitor(port='COM3', baudrate=115200)monitor.run()

七、实际应用中的注意事项

7.1 噪声处理

实际使用中,声学信号的噪声比较大。除了卡尔曼滤波,还可以考虑:

  1. 多次平均:对连续多个ping的结果取平均
  2. 中值滤波:剔除异常值
  3. 自适应滤波:根据SNR调整滤波参数

7.2 环境适应性

不同环境下声速会变化,需要根据温度、盐度修正:

float DVL_CalculateSoundSpeed(float temp, float salinity, float depth) {// Mackenzie公式float c = 1448.96f + 4.591f * temp - 0.05304f * temp * temp + 0.0002374f * temp * temp * temp+ (1.340f - 0.01025f * temp) * (salinity - 35.0f)+ 0.0163f * depth;return c;
}

7.3 数据融合

在实际导航系统中,DVL通常和IMU、GPS等传感器融合使用。可以用扩展卡尔曼滤波器(EKF)或者粒子滤波器实现多传感器融合。

7.4 硬件选型

如果要实际制作DVL,硬件方面需要注意:

  • 换能器:选择合适频率的换能器,通常300kHz~1.2MHz
  • ADC:至少需要2-4倍于载波频率的采样率,12位精度以上
  • 功放:发射端需要几十瓦的功率
  • MCU:需要较强的DSP能力,推荐使用STM32H7或F4系列

八、总结

这篇文章介绍了DVL的基本原理和代码实现。核心就是利用多普勒效应测量速度,通过四波束配置解算三维速度。代码实现了信号处理、频率估计、速度解算和滤波的完整流程。

实际项目中还有很多细节需要处理,比如波束选择、数据有效性判断、异常值处理等。商用DVL的算法会更复杂,还会考虑倾角补偿、波浪抑制等功能。

完整的代码已经上传到我的GitHub,欢迎star和讨论。如果你也在做水下机器人或者导航相关的项目,可以留言交流。

参考资料

  1. RD Instruments. ADCP Coordinate Transformation: Formulas and Calculations
  2. Teledyne Marine. WorkHorse Technical Manual
  3. 《水声学原理》- 刘伯胜
  4. 《卡尔曼滤波原理及应用》- 秦永元

本文所有代码均为原创,仅供学习参考,商用请自行承担风险。

http://www.dtcms.com/a/574699.html

相关文章:

  • vs怎么建手机网站网站开发开题报告范文2019
  • 迅为RK3576开发板编译环境Ubuntu20.04编译配置-修改物理内存
  • 岗贝路网站建设建设网站公司电话号码
  • 国内做网站多少钱特定ip段访问网站代码
  • Android控制三方音乐应用播放方案(实测可用)
  • 泰国金木棉做网站网站适合新手做的网站项目
  • 网站编辑面试问题和答案小程序源码分享
  • 怎么做pc端移动网站北京官方网站网
  • 文山网站建设兼职c 网站开发 书
  • 网站开发排行微网站建设方案财政
  • 为什么没人做物流网站郑州做网站哪个平台好
  • 26.序列模型
  • 安阳网站建设商祺wordpress qq登入设置
  • AtCoder Beginner Contest 430(ABCDEF)
  • 公关做的好的网站网络科技公司一般是做什么的
  • 高端制作网站服务用织梦做外文网站
  • postgres15 flink cdc同步测试
  • 做网站策划案自己做的腾讯充值网站
  • 网站建设经理岗位职责soho网站建设教程
  • 【数据集+完整源码】马数据集,马行为状态识别数据集 3006 张,yolov8目标检测牧场草原马识别算法实战训推教程
  • 网站开发公司云鲸互创实惠同企网站建设做网站
  • 企业网站建设上机考试开源网站后台管理系统
  • 工业互联网赋能——解读企业管理数字化转型架构设计思路【附全文阅读】
  • 行业网站解决方案免费做金融网站
  • [MT6631] MT6835+MT6631 5G WiFi hostapd信道与频宽配置说明
  • 手机网站建设开发报价页面设计按钮
  • 网站开发与维护介绍前端页面设计软件
  • Unity 显示git分支
  • 免费做相册视频网站荣县住房和城乡建设厅网站
  • 网站论坛建设需要什么资质可以免费做兼职的网站有哪些