总览
1.arduino 代码和库等...
2.Unity 的部分,创建一个 3D 工程,然后创建一个 cube,绑定一个脚本文件
3.效果预览:
【Arduino】BNO085 姿态的 3D模型 展示方法(映射到 Unity)
一、Arduino 部分
1.使用的硬件
· 单片机:使用了 ESP32S3,当然了,我没拿我的 atmega328P 去试,应该也可以。
· 使用的通讯方式:I2C
· 引脚连接方式:
ESP32S3 ----- ----- BNO085
3V3 ----- ----- VCC
GND ----- ----- GND
19 ----- ----- SDA
18 ----- ----- SCL
6 ----- ----- INT(看情况使用,我没使用)
2.软件部分
在使用此代码前,请搜索安装这个库:
代码片:
cpp
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BNO08x.h>
#define BNO08X_INT_PIN 6 // 使用 GPIO 6 作为中断引脚
#define I2C_SCL_PIN 18
#define I2C_SDA_PIN 19
Adafruit_BNO08x bno;
void setup() {
Serial.begin(115200); // 设置串口波特率
// 初始化 I2C 接口并指定引脚
Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
// 使用 I2C 地址 0x4B 初始化 BNO085
if (!bno.begin_I2C(0x4B)) {
Serial.println("BNO085 I2C 初始化失败!");
while (1);
}
Serial.println("BNO085 已经初始化");
// 启用需要的传感器报告
if (!bno.enableReport(SH2_GAME_ROTATION_VECTOR, 2500)) {
Serial.println("无法启用旋转向量报告");
}
}
void loop() {
if (digitalRead(BNO08X_INT_PIN) == LOW) {
sh2_SensorValue_t sensorValue;
// 读取传感器数据
if (bno.getSensorEvent(&sensorValue)) {
if (sensorValue.sensorId == SH2_GAME_ROTATION_VECTOR) {
// 获取旋转向量数据
float rotationX = sensorValue.un.gameRotationVector.i;
float rotationY = sensorValue.un.gameRotationVector.j;
float rotationZ = sensorValue.un.gameRotationVector.k;
// 通过串口发送数据到 Unity,格式为:ROTATION:X,Y,Z
Serial.print("ROTATION:");
Serial.print(rotationX); Serial.print(",");
Serial.print(rotationY); Serial.print(",");
Serial.print(rotationZ); Serial.println();
}
}
delay(5); // 添加延迟,越小精度越高且延迟越小,但负载也随之增加,个人建议 5 - 15 ms
}
}
二、Unity 部分的操作
1.创建一个 3D 工程
你需要下载安装好 Unity,然后打开 Unity HUB 创建一个 3D 工程。
2.创建一个 立方体
创建流程:左上角 GameObject >> 3D Object >> Cube
3.设置立方体位置 && 摄像机位置
你也可以不按照我的来,只是后面的参数你也要自己进行校准了。
· 立方体
· 摄像机
4.创建脚本文件
这是一个用于接收串口数据的脚本文件。
它的功能是接收单片机的串口数据,并且将弧度数据转为角度,映射在 cube 立方体上,实现姿态传感器可视化。
文件名:SerialReader.cs
编辑脚本文件代码(一般来说,我们使用 visual studio 打开它):
代码片:
csharp
using System;
using System.IO.Ports;
using UnityEngine;
public class SerialReader : MonoBehaviour
{
SerialPort serial = new SerialPort("COM3", 115200); // 请确认COM端口
public GameObject sensorObject; // 3D模型对象
// 放大比
// 规则说明:越大,立方体就越容易转动,越小就越不容易转动。
// 需要手动调节精度以配合 BNO085 的真实动作。
public float rotationMultiplier = 2.2f;
// 平滑过渡因子
// 规则说明:调节区间为( 0.0 ,1.0 ]
// 越大,平滑效果就越差,但是延迟越低;越小,平滑效果越好,但是延迟增高。
// 个人建议 0.5,如果觉得卡顿就 0.2,如果想要延迟更低就0.7
public float smoothingFactor = 0.5f;
private Quaternion targetRotation; // 目标旋转角度
void Start()
{
Debug.Log("SerialReader script started!");
try
{
serial.Open();
serial.ReadTimeout = 100;
Debug.Log("UART has opened!");
// 初始化目标旋转
targetRotation = sensorObject.transform.rotation;
}
catch (Exception e)
{
Debug.LogError("Error, can't open UART: " + e.Message);
}
}
void Update()
{
if (serial.IsOpen)
{
try
{
string data = serial.ReadLine();
Debug.Log("Received data: " + data);
if (data.StartsWith("ROTATION:"))
{
string[] values = data.Substring(9).Split(',');
// 反转 X 轴,并交换 Y 轴和 Z 轴
float rotX = -Mathf.Rad2Deg * float.Parse(values[0]) * rotationMultiplier; // 反转X轴
float rotY = -Mathf.Rad2Deg * float.Parse(values[2]) * rotationMultiplier; // 将Z轴数据用作Y轴
float rotZ = -Mathf.Rad2Deg * float.Parse(values[1]) * rotationMultiplier; // 将Y轴数据用作Z轴
// 计算目标旋转
targetRotation = Quaternion.Euler(rotX, rotY, rotZ);
}
}
catch (TimeoutException)
{
}
}
// 使用 Slerp 平滑过渡到目标旋转
sensorObject.transform.rotation = Quaternion.Slerp(sensorObject.transform.rotation, targetRotation, smoothingFactor);
}
void OnApplicationQuit()
{
if (serial.IsOpen)
{
serial.Close();
}
}
}
5.保存,不需要退出 visual studio
6.关联 cube 和 我们刚才创建的脚本文件 SerialReader.cs
点击立方体 >> 点击右侧最下栏的 "Add Component" >> 搜索 serial >> 选择 "Serial Reader"
点这个小圆点,然后选定我们的 cube
7.开始 RUN
至此,所有配置已结束,RUN 一下吧
请注意,如果大家在 RUN 的时候发现 cube 的动作太小,请关闭 Unity 程序,重新打开一次再运行
三、后记
1.如果我们旋转 BNO085 的角度 和 Unity 中 Cube 立方体的角度对不上怎么办?
代码中已经给大家留了可以手动调节放大比的地方。
2.立方体卡顿 || 有延迟怎么办
卡顿和延迟成反比关系,通过调节平滑因子来调节两者到合适的情况。
代码也预留了: