Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/doc/en/basic/upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Backup methods:

| Item | MaixCAM / MaixCAM-Pro | MaixCAM2 |
| --------------- | ---------- | ------ |
| Flashing Docs | [MaixCAM System Flashing](https://wiki.sipeed.com/hardware/zh/maixcam/os.html) | [MaixCAM2 System Flashing](https://wiki.sipeed.com/hardware/zh/maixcam/os_maixcam2.html) |
| Flashing Docs | [MaixCAM System Flashing](https://wiki.sipeed.com/hardware/zh/maixcam/os.html) | [MaixCAM2 System Flashing](https://wiki.sipeed.com/hardware/zh/maixcam/maixcam2_os.html) |
| System Storage | TF Card | Built-in EMMC (/TF Card) |
| TF Card Required | Yes | No |
| Flashing Method | USB flashing or card reader flashing | USB flashing or card reader flashing |
Expand Down
2 changes: 1 addition & 1 deletion docs/doc/zh/basic/upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ title: MaixCAM MaixPy 升级和烧录系统

| 项目 | MaixCAM / MaixCAM-Pro | MaixCAM2 |
| --- | --- | --- |
| 烧录文档 | [MaixCAM 系统烧录](https://wiki.sipeed.com/hardware/zh/maixcam/os.html) | [MaixCAM2 系统烧录](https://wiki.sipeed.com/hardware/zh/maixcam/os_maixcam2.html) |
| 烧录文档 | [MaixCAM 系统烧录](https://wiki.sipeed.com/hardware/zh/maixcam/os.html) | [MaixCAM2 系统烧录](https://wiki.sipeed.com/hardware/zh/maixcam/maixcam2_os.html) |
| 系统存放位置 | TF 卡 | 内置EMMC(/TF卡) |
| 必须 TF 卡 | 是 | 否 |
| 烧录方式 | USB 烧录 或 读卡器烧录 | USB 烧录 或 读卡器烧录 |
Expand Down
101 changes: 101 additions & 0 deletions examples/ext_dev/sensors/imu/acc_gyro_lsm6dsow/lsm6dsow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import smbus2
import struct
import numpy as np

class Lsm6dsow:
STATUS_REG = 0x1E
ACC_REG = 0x28
GYRO_REG = 0x22
CTRL1_XL = 0x10 # Accelerometer ODR/scale
CTRL2_G = 0x11 # Gyro ODR/scale

ACC_SENS_TABLE = {0:0.061, 1:0.122, 2:0.244, 3:0.488}
GYRO_SENS_TABLE = {0:8.75, 1:17.5, 2:35, 3:70}
GRAVITY = 9.80665
ODR_TABLE = [0, 12.5, 26, 52, 104, 208, 416, 833, 1660, 3330, 6660]

def __init__(self, i2c_bus=1, dev_addr=0x6b):
self.bus = smbus2.SMBus(i2c_bus)
self.addr = dev_addr
self._last_frame = np.zeros((2,3)) # [acc, gyro] last valid
self._last_updated = 0

def _read_reg(self, reg, length=1):
if length == 1:
return self.bus.read_byte_data(self.addr, reg)
return self.bus.read_i2c_block_data(self.addr, reg, length)

def _write_reg(self, reg, value):
self.bus.write_byte_data(self.addr, reg, value)

def get_acc_odr(self):
val = self._read_reg(self.CTRL1_XL)
odr_idx = (val >> 4) & 0x0F
return self.ODR_TABLE[odr_idx] if odr_idx < len(self.ODR_TABLE) else 0

def set_acc_odr(self, hz):
idx = min(range(len(self.ODR_TABLE)), key=lambda i: abs(self.ODR_TABLE[i]-hz))
val = self._read_reg(self.CTRL1_XL)
val = (val & 0x0F) | (idx << 4)
self._write_reg(self.CTRL1_XL, val)

def get_gyro_odr(self):
val = self._read_reg(self.CTRL2_G)
odr_idx = (val >> 4) & 0x0F
return self.ODR_TABLE[odr_idx] if odr_idx < len(self.ODR_TABLE) else 0

def set_gyro_odr(self, hz):
idx = min(range(len(self.ODR_TABLE)), key=lambda i: abs(self.ODR_TABLE[i]-hz))
val = self._read_reg(self.CTRL2_G)
val = (val & 0x0F) | (idx << 4)
self._write_reg(self.CTRL2_G, val)

def get_acc_scale(self):
val = self._read_reg(self.CTRL1_XL)
fs = (val >> 2) & 0x03
return self.ACC_SENS_TABLE.get(fs, 0.061)

def set_acc_scale(self, g_val):
fs_map = {2:0, 4:1, 8:2, 16:3}
fs = fs_map.get(g_val, 0)
val = self._read_reg(self.CTRL1_XL)
val = (val & ~(0x0C)) | (fs << 2)
self._write_reg(self.CTRL1_XL, val)

def get_gyro_scale(self):
val = self._read_reg(self.CTRL2_G)
fs = (val >> 2) & 0x03
return self.GYRO_SENS_TABLE.get(fs, 8.75)

def set_gyro_scale(self, dps_val):
fs_map = {250:0, 500:1, 1000:2, 2000:3}
fs = fs_map.get(dps_val, 0)
val = self._read_reg(self.CTRL2_G)
val = (val & ~(0x0C)) | (fs << 2)
self._write_reg(self.CTRL2_G, val)

def read(self):
status = self._read_reg(self.STATUS_REG)
updated = 0
if (status & 0x01) and (status & 0x02):
acc_bytes = self._read_reg(self.ACC_REG, 6)
gyro_bytes = self._read_reg(self.GYRO_REG, 6)
acc_raw = np.array(struct.unpack('<hhh', bytes(acc_bytes)), dtype=np.int16)
gyro_raw = np.array(struct.unpack('<hhh', bytes(gyro_bytes)), dtype=np.int16)
acc_scale = self.get_acc_scale()
gyro_scale = self.get_gyro_scale()
acc_val = acc_raw * acc_scale * self.GRAVITY / 1000 # m/s²
gyro_val = gyro_raw * gyro_scale / 1000 # °/s
self._last_frame = np.array([acc_val, gyro_val])
updated = 1
else:
# No new data, return last
updated = 0
return updated, self._last_frame.copy()

def read_raw(self):
acc_bytes = self._read_reg(self.ACC_REG, 6)
gyro_bytes = self._read_reg(self.GYRO_REG, 6)
acc_raw = np.array(struct.unpack('<hhh', bytes(acc_bytes)), dtype=np.int16)
gyro_raw = np.array(struct.unpack('<hhh', bytes(gyro_bytes)), dtype=np.int16)
return np.array([acc_raw, gyro_raw])
113 changes: 113 additions & 0 deletions examples/ext_dev/sensors/imu/acc_gyro_lsm6dsow/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
from maix import display, app, image
disp = display.Display()
print("display init done")
print(f"display size: {disp.width()}x{disp.height()}")

######################################
import cv2
import numpy as np
# 画布大小
w, h = disp.width(), disp.height()
# 坐标中心
center = np.array([w//2, h//2])

# 构造旋转矩阵: 把原始Z轴(0,0,1)转到acc_norm方向
def rotation_matrix_from_vectors(a, b):
a = a / np.linalg.norm(a)
b = b / np.linalg.norm(b)
v = np.cross(a, b)
c = np.dot(a, b)
s = np.linalg.norm(v)
if s < 1e-8:
return np.eye(3)
kmat = np.array([[ 0, -v[2], v[1]],
[ v[2], 0, -v[0]],
[-v[1], v[0], 0]])
return np.eye(3) + kmat + kmat @ kmat * ((1-c)/(s**2))

# 原始坐标轴单位向量
axes = {
'X': np.array([1,0,0]),
'Y': np.array([0,1,0]),
'Z': np.array([0,0,1])
}
colors = {'X': (0,0,255), 'Y': (0,255,0), 'Z': (255,0,0)}

# 投影到画布(简单正交投影,忽略Z)
def project(vec):
scale = 150
x = int(center[0] + vec[0]*scale)
y = int(center[1] - vec[1]*scale)
return (x, y)
######################################

import time
import lsm6dsow
imu = lsm6dsow.Lsm6dsow()
imu.set_acc_odr(imu.ODR_TABLE[7])
imu.set_gyro_odr(imu.ODR_TABLE[7])
imu.set_acc_scale(imu.ACC_SENS_TABLE[0])
imu.set_gyro_scale(imu.GYRO_SENS_TABLE[0])

count = 0
duration = 180.0 # 统计180秒
start = time.time()
while not app.need_exit():
# 新建画布
canvas = np.ones((h, w, 3), dtype=np.uint8) * 255

remain_time = start + duration - time.time()
if remain_time < 0:
break
else:
cv2.putText(canvas, f"Auto exit after {remain_time:.1f}s", (20,30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255,0,0), 2)

# 同时采样加速度和陀螺仪
upd, acc_gyro_vals = imu.read()
if upd:
# print("ACC(m/s²):", " ".join("{:8.4f}".format(x) for x in acc_vals),
# "GYRO(°/s):", " ".join("{:8.4f}".format(x) for x in gyro_vals))
count += 1
acc_vals = acc_gyro_vals[0]
gyro_vals = acc_gyro_vals[1]

# 1. 示例加速度(m/s²)和角速度(°/s),请替换为你的实际数据
acc = np.array(acc_vals) # X, Y, Z
gyro = np.array(gyro_vals) # X, Y, Z

# 2. 归一化加速度作为“新Z轴方向”
acc_norm = acc / np.linalg.norm(acc) if np.linalg.norm(acc) > 1e-5 else np.array([0,0,1])

# 3. 计算旋转后的坐标轴
R = rotation_matrix_from_vectors(np.array([0,0,1]), acc_norm)
rot_axes = {k: R @ v for k,v in axes.items()}

# 4. 画三轴
for k in ['X', 'Y', 'Z']:
tip = project(rot_axes[k])
cv2.arrowedLine(canvas, tuple(center), tip, colors[k], 3, tipLength=0.10)
cv2.putText(canvas, k, tip, cv2.FONT_HERSHEY_SIMPLEX, 0.7, colors[k], 2)

# 5. 角速度箭头可视化(方向和长度)
gyro_body = gyro / (np.linalg.norm(gyro)+1e-8) if np.linalg.norm(gyro)>1e-8 else np.array([0,0,1])
gyro_len = min(float(np.linalg.norm(gyro)), 1.0) * 100 # 可调整箭头长度比例
# 6. 旋转到加速度系
gyro_proj = R @ gyro_body
gyro_tip = project(gyro_proj * (gyro_len/150)) # 按长度比例缩放

cv2.arrowedLine(canvas, tuple(center), gyro_tip, (0,0,0), 4, tipLength=0.15)
cv2.putText(canvas, "gyro", gyro_tip, cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,0,0), 2)

# 7. 显示数值
cv2.putText(canvas, f"acc: [{acc[0]:.2f}, {acc[1]:.2f}, {acc[2]:.2f}]", (20,60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,0,0), 2)
cv2.putText(canvas, f"gyro: [{gyro[0]:.2f}, {gyro[1]:.2f}, {gyro[2]:.2f}]", (20,90), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,0,0), 2)

fps = count / (time.time() - start)
cv2.putText(canvas, f"fps: {fps:.2f}", (20,120), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,255,0), 2)

img = image.cv2image(canvas)
disp.show(img)

rate = count / duration
print(f"有效采集速率: {rate:.2f} Hz")

2 changes: 1 addition & 1 deletion examples/ext_dev/sensors/tiny1c/tiny1c_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,10 +252,10 @@ def scale_to_range(arr, target_min, target_max):

if PREVIEW_TEMP:
gray = np.frombuffer(image_frame, dtype=np.uint16).reshape((height, width))
gray = cv2.rotate(gray, cv2.ROTATE_180)

# 归一化到8位(0~255)
img_8bit = cv2.normalize(gray >> 2, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
img_8bit = cv2.rotate(img_8bit, cv2.ROTATE_180)
img_8bit_raw_min_max = (img_8bit.min(), img_8bit.max())

if enable_x3:
Expand Down
46 changes: 38 additions & 8 deletions examples/ext_dev/sensors/tof100/tof100_example.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,49 @@
from maix import display, app, time, ext_dev
from maix import display, image, app, time, ext_dev

from maix import pinmap

import numpy as np
import cv2

pin_function = {
"A8": "I2C7_SCL",
"A9": "I2C7_SDA",
"B21": "SPI2_CS1",
"B19": "SPI2_MISO",
"B18": "SPI2_MOSI",
"B20": "SPI2_SCK"
}

for pin, func in pin_function.items():
if 0 != pinmap.set_pin_function(pin, func):
print(f"Failed: pin{pin}, func{func}")
exit(-1)

disp = display.Display()

tof = ext_dev.tof100.Tof100(
4,
ext_dev.tof100.Resolution.RES_50x50,
2,
ext_dev.tof100.Resolution.RES_100x100,
ext_dev.cmap.Cmap.JET,
40, 1000)

t0 = time.time()
while not app.need_exit():
img = tof.image()
if img is not None:
disp.show(img)
print("min: ", tof.min_dis_point())
print("max: ", tof.max_dis_point())
print("center: ", tof.center_point())
img_bgr = image.image2cv(img, ensure_bgr=True, copy=True)
img_bgr = cv2.rotate(img_bgr, cv2.ROTATE_180)
img_bgr = cv2.resize(img_bgr, (400, 400))

scr = np.zeros((disp.height(), disp.width(), 3), dtype=np.uint8)
scr[:img_bgr.shape[0], :img_bgr.shape[1], ...] = img_bgr
disp.show(image.cv2image(scr))
fps = time.fps()
print(f"time: {1000/fps:.02f}ms, fps: {fps:.02f}")
t1 = time.time()
if t1-t0>1:
print("min: ", tof.min_dis_point())
print("max: ", tof.max_dis_point())
print("center: ", tof.center_point())
print(f"time: {1000/fps:.02f}ms, fps: {fps:.02f}")
print(f"t0:{t0}, t1:{t1}")
t0=t1
3 changes: 3 additions & 0 deletions projects/app_imu_lsm6dsow/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
data
__pycache__
dist
11 changes: 11 additions & 0 deletions projects/app_imu_lsm6dsow/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
IMU AHRS 姿态解算
=====


Use integrated IMU to get device status(euler angles).

APP: [https://maixhub.com/app/128](https://maixhub.com/app/128)




18 changes: 18 additions & 0 deletions projects/app_imu_lsm6dsow/app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
id: imu_ahrs
name: IMU AHRS
name[zh]: 姿态解算
version: 1.0.3
icon: ''
author: Sipeed Ltd
desc: IMU AHRS
desc[zh]: IMU 姿态解算
exclude:
- dist
- build
- .gitignore
files:
- assets/icon.png
- app.yaml
- lsm6dsow.py
- main.py
- README.md
Binary file added projects/app_imu_lsm6dsow/assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading