自己攒op电脑版之IMU模块-GY95T
之前我写在知识星球上的,搬运到这里吧
在淘宝上买了一个 GY-95T九轴传感器 ,样子是这样的 👇
店家发的程序是Windows下面的,焊接好后插上,找到COM口,选好就能用了。但是这在Linux下跑不了啊,更别说我们要用程序读取数据了,于是我打开了DeepSeek、ChatGPT、Grok…
经过一天折腾后,我终于可以在Linux下把玩这个小玩具了🐶
正文开始,要在Linux(Ubuntu系统,其他应该一样)下愉快的使用,需要下面几步
1. 检查串口通信模块
lsmod | grep -E 'pl2303|ch341'
如果没有输出,那么就执行sudo modprobe ch341
再用上面的检查一下,如果还是没有输出,你要在内核中开启这个了,下面的不用看了,去编译内核吧。。。
2. 删除 brltty
咱也不知道这玩意为啥和gy95t串口通信冲突了,查了一下是跟色盲有关的,一般人也用不到,删掉完事
sudo apt-get remove brltty
如果你恰好用到了,那么可以用 echo ‘deny-usb-ids 1a86:7523’ >> /etc/brltty.conf 避免冲突(我没测试,AI说的)
然后重启一下服务 sudo systemctl restart brltty
3. 添加 udev rules
添加这个可以创建一个 /dev/gy95t的节点,我们用这个避免和其他人发生冲突
1 | echo 'SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", MODE="0666", GROUP="dialout", SYMLINK+="gy95t"' |sudo tee /etc/udev/rules.d/99-gy95t.rules |
搞完上面三步之后,下面的代码才能跑起来。代码主要来自 https://blog.csdn.net/m0_73694897/article/details/131273512
点我展开看代码
import serial # 导入串口库
import struct
import binascii
import time
# imu类
class IMU():
send_data = []
def init(self):
# 串口初始化
self.IMU_Usart = serial.Serial(
port = ‘/dev/gy95t’, # 根据自己的串口修改串口名
baudrate=115200, # 波特率
timeout = 0.001 # read_all按照一个timeout周期时间读取数据
)
# 接收数据初始化
self.ACC_X:float = 0.0 # X轴加速度
self.ACC_Y:float = 0.0 # Y轴加速度
self.ACC_Z:float = 0.0 # Z轴加速度
self.GYRO_X :float = 0.0 # X轴陀螺仪
self.GYRO_Y :float = 0.0 # Y轴陀螺仪
self.GYRO_Z :float = 0.0 # Z轴陀螺仪
self.roll :float = 0.0 # 横滚角
self.pitch :float = 0.0 # 俯仰角
self.yaw :float = 0.0 # 航向角
self.leve :float = 0.0 # 磁场校准精度
self.temp :float = 0.0 # 器件温度
self.MAG_X :float = 0.0 # 磁场X轴
self.MAG_Y :float = 0.0 # 磁场Y轴
self.MAG_Z :float = 0.0 # 磁场Z轴
self.Q0 :float = 0.0 # 四元数Q0.0
self.Q1 :float = 0.0 # 四元数Q1
self.Q2 :float = 0.0 # 四元数Q2
self.Q3 :float = 0.0 # 四元数Q3
# 判断串口是否打开成功
if self.IMU_Usart.isOpen():
print(“串口打开成功!”)
else:
print(“串口打开失败!”)
# 发送读取指令
self.Send_ReadCommand()
# 回调函数返回周期
time_period = 0.001
def Send_ReadCommand(self):
#发送读取IMU内部数据指令
#读寄存器例子,读取模块内部温度,主站发送帧为:A4 03 1B 02 C4
# | A4 | 03 | 1B | 02 | C4
# | 帧头ID | 读功能码 |起始寄存器| 寄存器数量 |校验和低 8 位
#0xA4:帧头 0x03:读取 0x08:第一个数据寄存器08 0x23:共35个数据寄存器 0xD2:前面数据的和取低八位
send_data = [0xA4,0x03,0x08,0x23,0xD2] #读35个寄存器需要发送的串口包
#send_data = [0xA4,0x03,0x08,0x1B,0xCA] #读27个寄存器需要发送的串口包,不发送串口包默认的接收状态
send_data=struct.pack(“%dB”%(len(send_data)),send_data) #解析成16进制
print(“向imu发送命令:”,send_data) #显示发送给imu的数据
self.IMU_Usart.write(send_data) #发送
def Read_data(self):
#读取数据
# 初始化数据
counter = 0
Recv_flag = 0
Read_buffer = []
# 接收数据至缓存区
Read_buffer=self.IMU_Usart.read(40) # 我们需要读取的是40个寄存器数据,即40个字节
# 状态机判断收包数据是否准确
while(1):
data_len = 0
if not Read_buffer:
print(“no data, retry”)
time.sleep(0.1)
return
# 第1帧是否是帧头ID 0xA4
if (counter == 0):
if(Read_buffer[0] != 0xA4):
break
# 第2帧是否是读功能码 0x03
elif (counter == 1):
if(Read_buffer[1] != 0x03):
counter=0
break
# 第3帧判断起始帧
elif (counter == 2):
if(Read_buffer[2] < 0x2c):
start_reg=Read_buffer[2]
else:
counter=0
# 第4帧判断帧有多少数量
elif (counter == 3):
if((start_reg+Read_buffer[3]) < 0x2C): # 最大寄存器为2C 大于0x2C说明数据肯定错了
data_len=Read_buffer[3]
else:
counter=0
break
else:
if(data_len+5==counter):
#print(‘Recv done!’)
Recv_flag=1
# 收包完毕
if(Recv_flag):
Recv_flag = 0
sum = 0
#print(Read_buffer) # Read_buffer中的是byte数据字节流,用struct包解包
data_inspect = str(binascii.b2a_hex(Read_buffer)) # data是将数据转化为原本的按照16进制的数据
#print(data_inspect)
try: # 如果接收数据无误,则执行数据解算操作
for i in range(2,80,2): # 根据手册,检验所有帧之和低八位是否等于末尾帧
sum += int(data_inspect[i:i+2],16)
if (str(hex(sum))[-2:] == data_inspect[80:82]): # 如果数据检验没有问题,则进入解包过程
#print(‘the Rev data is right’)
# 数据低八位在前,高八位在后
#print(Read_buffer[4:-1])
unpack_data = struct.unpack(‘<hhhhhhhhhBhhhhhhhh’,Read_buffer[4:-1])
# 切片并将其解析为我们所需要的数据,切出我们所需要的数据部分
g = 9.8
self.ACC_X = unpack_data[0]/2048 g # unit m/s^2
self.ACC_Y = unpack_data[1]/2048 g
self.ACC_Z = unpack_data[2]/2048 g
self.GYRO_X = unpack_data[3]/16.4 # unit 度/s
self.GYRO_Y = unpack_data[4]/16.4
self.GYRO_Z = unpack_data[5]/16.4
self.roll = unpack_data[6]/100
self.pitch = unpack_data[7]/100
self.yaw = unpack_data[8]/100
self.level = unpack_data[9]
self.temp = unpack_data[10]/100
self.MAG_X = unpack_data[11]/1000 # unit Gaos
self.MAG_Y = unpack_data[12]/1000
self.MAG_Z = unpack_data[13]/1000
self.Q0 = unpack_data[14]/10000
self.Q1 = unpack_data[15]/10000
self.Q2 = unpack_data[16]/10000
self.Q3 = unpack_data[17]/10000
print(self.dict)
except KeyboardInterrupt:
if serial != None:
print(“关闭串口端口”)
self.IMU_Usart.close()
except:
print(“接收的数据有错误!!”)
counter=0
break
else:
counter += 1 # 遍历整个接收数据的buffer
def timer_callback(self):
# —-读取IMU的内部数据———————————–
try:
count = self.IMU_Usart.inWaiting()
if count > 0:
self.Read_data()
# 发布sensor_msgs/Imu 数据类型
imu_data = Imu()
imu_data.header.frame_id = “map”
imu_data.header.stamp = self.get_clock().now().to_msg()
imu_data.linear_acceleration.x = self.ACC_X
imu_data.linear_acceleration.y = self.ACC_Y
imu_data.linear_acceleration.z = self.ACC_Z
imu_data.angular_velocity.x = self.GYRO_X 3.1415926 / 180.0 # unit transfer to rad/s
imu_data.angular_velocity.y = self.GYRO_Y 3.1415926 / 180.0
imu_data.angular_velocity.z = self.GYRO_Z * 3.1415926 / 180.0
imu_data.orientation.x = self.Q0
imu_data.orientation.y = self.Q1
imu_data.orientation.z = self.Q2
imu_data.orientation.w = self.Q3
self.publisher_.publish(imu_data) # 发布imu的数据
# ——————————————————–
#print(‘imu数据发布中’)
except KeyboardInterrupt:
if serial != None:
print(“关闭串口端口”)
self.IMU_Usart.close()
#——————————————————–
def main(args=None):
# 变量初始化———————————————
imu = IMU()
while True:
imu.Read_data()
time.sleep(0.1)
if name == ‘main‘:
main()
执行后就可以看到下面的数据,表面可以正常读取
1 | bird@bird-GE60-2OC-2OE:~$ python3 usart.py |
关于如何在op上使用,你应该会了吧 🌹🐔
上次写完竟然被质疑能不能用 😓
有些代码还是要自己写的,这个python的只是gy95t这一款传感器使用的一种方式,用i2c接入的就是另外的代码了
关于怎么接入op,可以参考官方的代码 https://github.com/commaai/openpilot/blob/c3bba7431ab1b6dd1b2e5b0e937a446b606e4e67/tools/sim/lib/simulated_sensors.py#L23-L40
很简单,就不写了 🌹🐔


