项目介绍
随着虚拟现实(VR)技术的快速发展,体感追踪成为了提升用户体验的重要环节。体感追踪器可以捕捉用户的身体动作,并实时将这些动作映射到虚拟环境中,使用户能够以更加自然和沉浸的方式与虚拟世界互动。为了实现这一目标,传感器技术的进步提供了强有力的支持。
本项目旨在利用X-NUCLEO-IKS4A1传感器扩展板上的惯性测量单元(IMU)、磁场传感器和气压传感器,设计并制作一个VR体感追踪器。通过这些传感器的结合,我们可以实现高精度的空间姿态计算以及相对海拔高度变化检测,并将这些数据实时传输到电脑上,为VR应用提供可靠的体感追踪解决方案。
设计思路
- 传感器数据采集
- 使用X-NUCLEO-IKS4A1传感器扩展板,该板集成了IMU(加速度计和陀螺仪)、磁场传感器和气压传感器。
- 利用NUCLEO-G0B1RE开发板进行数据处理和传输。
- 空间姿态计算
- 利用IMU数据(加速度和角速度)计算板卡的空间姿态。姿态解算采用MotionFX库中的算法实现,可提供实时运动传感器数据融合。
- 磁场传感器提供的地磁数据可用于校正IMU计算的姿态,减小累积误差。
- 相对海拔高度变化检测
- 气压传感器用于测量环境气压,并使用hypsometric公式通过气压和温度数据来计算海拔高度。
- 数据传输
- 利用NUCLEO-G0B1RE开发板的串口功能,实现板卡与电脑之间的有线数据传输。
- 设计合适的通讯协议帧,确保传感器数据能够实时、稳定地传输到电脑,并可视化展示。
硬件介绍
X-NUCLEO-IKS4A1
X-NUCLEO-IKS4A1 是一款先进的运动 MEMS 和环境传感器评估板套件,包括 X-NUCLEO-IQS4A1 主板和可拆卸的 STEVAL-MKE001A 附加板。主板搭载运动 MEMS 和环境传感器,而附加板配备 Qvar 滑动电极,提供触摸和滑动手势识别功能。该扩展板支持各种应用开发,具备传感器 HUB(LSM6DSO16IS 和 LSM6DSV16X)和 Qvar 技术,还能集成 IR 传感器实现存在和运动检测。
主要传感器包括:
- LSM6DSO16IS:MEMS 3D 加速度计 (±2/±4/±8/±16 g) 和 3D 陀螺仪 (±125/±250/±500/±1000/±2000 dps),带智能处理单元(ISPU)
- LIS2MDL:MEMS 3D 磁力计 (±50 gauss)
- LIS2DUXS12:超低功耗 MEMS 3 轴加速度计 (±2/±4/±8/±16 g),具备 Qvar、AI 和抗混叠功能
- LPS22DF:低功耗高精度 MEMS 压力传感器,260-1260 hPa 绝对气压计
- SHT40AD1B:高精度湿度传感器
- STTS22H:超低功耗温度传感器,测量范围 -40 °C 到 +125 °C,精度 0.5°C
- LSM6DSV16X:MEMS 3D 加速度计 (±2/±4/±8/±16 g) 和 3D 陀螺仪 (±125/±250/±500/±1000/±2000/±4000 dps),支持传感器融合、AI 和 Qvar 技术
此套件为开发者提供了丰富的传感功能和应用场景支持,是一个集成度高、功能全面的评估平台。
NUCLEO-G0B1RE
STM32 Nucleo-G0B1RE 开发板为用户提供了一种经济实惠且灵活的方法,利用 STM32G0B1RE 微控制器的多种性能和低功耗特性,来尝试新概念并构建原型。该板基于 STM32G0 系列微控制器,具有丰富的外设和接口,适用于各种嵌入式应用。
主要特性和优势
- 微控制器:搭载 STM32G0B1RE 微控制器,具有 ARM® Cortex®-M0+ 内核,主频高达 64 MHz,内置 512 KB Flash 和 144 KB RAM,提供强大的处理能力和内存空间。
- 电源管理:支持外部 SMPS(开关模式电源),显著降低运行模式下的功耗,提升能效。
- 扩展能力:
- ARDUINO® Uno V3 连接支架:兼容众多 Arduino 生态系统的屏蔽板,方便快速扩展功能。
- ST morpho 接头:提供额外的 GPIO 引脚,支持更多的外设和扩展模块连接,进一步增强开发板的灵活性和功能。
- 集成调试和编程:内置 ST-LINK/V2-1 调试器/编程器,无需外部探头,支持在线调试和固件下载,简化开发流程。
- 丰富的外设接口:提供多种外设接口,包括 USART、SPI、I2C、ADC 和 DAC,适应不同应用需求。
- 易于使用:兼容广泛的开发环境,如 STM32CubeIDE、IAR EWARM、Keil MDK-ARM 等,支持 STM32CubeMX 配置工具,简化开发和配置过程。
软件说明
软件部分主要分为嵌入式软件代码和上位机软件两部分。
软件流程图
嵌入式主要功能代码
传感器数据采集以及数据融合代码如下,首先获取传感器的加速度和角加速度数据,之后使用st的motionfx库进行传感器融合,得到俯仰角 偏航角 滚转角三个角度数据,之后获取传感器的温度和气压数据。最后将其通过串口发送到电脑端。注意的是这里的气压单位为hpa,在计算海拔高度时需要将其转换为kpa。
void fusion_update(void)
{
MFX_CM0P_input_t data_in;
MFX_CM0P_output_t data_out;
AccGyr.Get_X_Axes(accelerometer);
AccGyr.Get_G_Axes(gyroscope);
LPS22DF.GetPressure(&pressure);
LPS22DF.GetTemperature(&temperature);
/* Convert angular velocity from [mdps] to [dps] */
data_in.gyro[0] = (float)gyroscope[0] * FROM_MDPS_TO_DPS;
data_in.gyro[1] = (float)gyroscope[1] * FROM_MDPS_TO_DPS;
data_in.gyro[2] = (float)gyroscope[2] * FROM_MDPS_TO_DPS;
/* Convert acceleration from [mg] to [g] */
data_in.acc[0] = (float)accelerometer[0] * FROM_MG_TO_G;
data_in.acc[1] = (float)accelerometer[1] * FROM_MG_TO_G;
data_in.acc[2] = (float)accelerometer[2] * FROM_MG_TO_G;
/* Don't set mag values because we use only acc and gyro */
data_in.mag[0] = 0.0f;
data_in.mag[1] = 0.0f;
data_in.mag[2] = 0.0f;
MotionFX_CM0P_update(&data_out, &data_in, MOTION_FX_ENGINE_DELTATIME);
Serial.print("Orientation: ");
Serial.print(data_out.rotation_6X[0]);//Yaw
Serial.print(", ");
Serial.print(data_out.rotation_6X[1]);//Pitch
Serial.print(", ");
Serial.print(data_out.rotation_6X[2]);//Roll
Serial.print(", ");
Serial.print(temperature);//Roll
Serial.print(", ");
Serial.println(pressure, 2);
}
上位机主要代码
首先创建了一个类,用于获取并存储串口接收到的角度以及温度湿度等信息。
class SerialFusion_Data:
def __init__(self, serial_port="COM16", baudrate=115200, timeout=2):
# super(SerialFusion_Data, self).__init__()
self.serial = None
self.serial_port = serial_port
self.serial_baudrate = baudrate
self.serial_timeout = timeout
self.Roll = 0
self.Pitch = 0
self.Yaw = 0
self.Temperature = 0
self.Pressure = 0
self.Altitude = 0
def serial_init(self):
self.serial = serial.Serial(self.serial_port, baudrate=self.serial_baudrate, timeout=self.serial_timeout)
def read_data(self):
while True:
# 读取一行数据
line = self.serial.readline().decode('utf-8').strip()
numbers = re.findall(r"[-+]?\d*\.\d+|\d+", line)
# 将字符串转换为浮点数
quaternion = [float(num) for num in numbers]
# print(quaternion)
self.Yaw = quaternion[0]
self.Pitch = quaternion[1]
self.Roll = quaternion[2]
self.Temperature = quaternion[3]
self.Pressure = quaternion[4] / 10 # Kpa
self.Altitude = calculate_altitude(self.Pressure, self.Temperature)
print(quaternion)
print(self.Altitude)
此外,在上位机使用hypsometric公式通过气压和温度数据来计算海拔高度,具体表达式如下:h = [ ( P_0 / P )^(1 / 5.257) - 1 ] × ( T + 273.15 ) / 0.0065。
其中,P_0是标准大气压强,取值为101.325 kPa;P是实际测量的大气压强,单位为kPa;T是实际测量的温度,单位为摄氏度。在这个公式中,将温度从摄氏度转换为开尔文。这个公式同时考虑了温度和压强来计算海拔高度。
def calculate_altitude(pressure, temperature):
standard_pressure = 1013.25
altitude = ((standard_pressure / pressure) ** (1 / 5.257) - 1) * (temperature + 273.15) / 0.0065
altitude = (1 - (pressure / standard_pressure) ** (1 / 5.257)) * (temperature + 273.15)
return altitude
主函数如下,使用tkinter设计串口选择窗口,并使用OpenGL绘制三维图像。
root = tk.Tk()
display = (640, 480)
fusion = SerialFusion_Data()
port_gui = PyIMU_3Dvisualizer(root)
port_gui.run()
if port_gui.ApplicationGL:
fusion_gui_init()
try:
myThread1 = threading.Thread(target=fusion.read_data)
myThread1.daemon = True
myThread1.start()
while True:
event = pygame.event.poll()
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
break
draw_fusion()
pygame.time.wait(10)
except:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
draw_text("Sorry, something is wrong :c")
pygame.display.flip()
time.sleep(5)
功能展示及说明
图1 上位机界面1
图2 上位机界面2
心得体会
在本项目中,我学到了如何结合惯性测量单元(IMU)、磁场传感器和气压传感器进行高精度空间姿态计算和相对海拔高度变化检测。尽管项目取得了一定的成果,但在传感器数据的校准和融合方面的更深层的知识还不没太学习清楚,在接下来的时间里还会继续学习。最后,非常感谢硬禾提供的支持和资源,使我有机会接触和学习到姿态融合的知识。