Skip to content

Commit 6f5c292

Browse files
committed
Merge branch 'pr-155' into dev
2 parents 55e784c + d240d0e commit 6f5c292

File tree

10 files changed

+688
-12
lines changed

10 files changed

+688
-12
lines changed

docs/doc/en/basic/upgrade.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Backup methods:
4141

4242
| Item | MaixCAM / MaixCAM-Pro | MaixCAM2 |
4343
| --------------- | ---------- | ------ |
44-
| 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) |
44+
| 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) |
4545
| System Storage | TF Card | Built-in EMMC (/TF Card) |
4646
| TF Card Required | Yes | No |
4747
| Flashing Method | USB flashing or card reader flashing | USB flashing or card reader flashing |

docs/doc/zh/basic/upgrade.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ title: MaixCAM MaixPy 升级和烧录系统
3737

3838
| 项目 | MaixCAM / MaixCAM-Pro | MaixCAM2 |
3939
| --- | --- | --- |
40-
| 烧录文档 | [MaixCAM 系统烧录](https://wiki.sipeed.com/hardware/zh/maixcam/os.html) | [MaixCAM2 系统烧录](https://wiki.sipeed.com/hardware/zh/maixcam/os_maixcam2.html) |
40+
| 烧录文档 | [MaixCAM 系统烧录](https://wiki.sipeed.com/hardware/zh/maixcam/os.html) | [MaixCAM2 系统烧录](https://wiki.sipeed.com/hardware/zh/maixcam/maixcam2_os.html) |
4141
| 系统存放位置 | TF 卡 | 内置EMMC(/TF卡) |
4242
| 必须 TF 卡 |||
4343
| 烧录方式 | USB 烧录 或 读卡器烧录 | USB 烧录 或 读卡器烧录 |
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import smbus2
2+
import struct
3+
import numpy as np
4+
5+
class Lsm6dsow:
6+
STATUS_REG = 0x1E
7+
ACC_REG = 0x28
8+
GYRO_REG = 0x22
9+
CTRL1_XL = 0x10 # Accelerometer ODR/scale
10+
CTRL2_G = 0x11 # Gyro ODR/scale
11+
12+
ACC_SENS_TABLE = {0:0.061, 1:0.122, 2:0.244, 3:0.488}
13+
GYRO_SENS_TABLE = {0:8.75, 1:17.5, 2:35, 3:70}
14+
GRAVITY = 9.80665
15+
ODR_TABLE = [0, 12.5, 26, 52, 104, 208, 416, 833, 1660, 3330, 6660]
16+
17+
def __init__(self, i2c_bus=1, dev_addr=0x6b):
18+
self.bus = smbus2.SMBus(i2c_bus)
19+
self.addr = dev_addr
20+
self._last_frame = np.zeros((2,3)) # [acc, gyro] last valid
21+
self._last_updated = 0
22+
23+
def _read_reg(self, reg, length=1):
24+
if length == 1:
25+
return self.bus.read_byte_data(self.addr, reg)
26+
return self.bus.read_i2c_block_data(self.addr, reg, length)
27+
28+
def _write_reg(self, reg, value):
29+
self.bus.write_byte_data(self.addr, reg, value)
30+
31+
def get_acc_odr(self):
32+
val = self._read_reg(self.CTRL1_XL)
33+
odr_idx = (val >> 4) & 0x0F
34+
return self.ODR_TABLE[odr_idx] if odr_idx < len(self.ODR_TABLE) else 0
35+
36+
def set_acc_odr(self, hz):
37+
idx = min(range(len(self.ODR_TABLE)), key=lambda i: abs(self.ODR_TABLE[i]-hz))
38+
val = self._read_reg(self.CTRL1_XL)
39+
val = (val & 0x0F) | (idx << 4)
40+
self._write_reg(self.CTRL1_XL, val)
41+
42+
def get_gyro_odr(self):
43+
val = self._read_reg(self.CTRL2_G)
44+
odr_idx = (val >> 4) & 0x0F
45+
return self.ODR_TABLE[odr_idx] if odr_idx < len(self.ODR_TABLE) else 0
46+
47+
def set_gyro_odr(self, hz):
48+
idx = min(range(len(self.ODR_TABLE)), key=lambda i: abs(self.ODR_TABLE[i]-hz))
49+
val = self._read_reg(self.CTRL2_G)
50+
val = (val & 0x0F) | (idx << 4)
51+
self._write_reg(self.CTRL2_G, val)
52+
53+
def get_acc_scale(self):
54+
val = self._read_reg(self.CTRL1_XL)
55+
fs = (val >> 2) & 0x03
56+
return self.ACC_SENS_TABLE.get(fs, 0.061)
57+
58+
def set_acc_scale(self, g_val):
59+
fs_map = {2:0, 4:1, 8:2, 16:3}
60+
fs = fs_map.get(g_val, 0)
61+
val = self._read_reg(self.CTRL1_XL)
62+
val = (val & ~(0x0C)) | (fs << 2)
63+
self._write_reg(self.CTRL1_XL, val)
64+
65+
def get_gyro_scale(self):
66+
val = self._read_reg(self.CTRL2_G)
67+
fs = (val >> 2) & 0x03
68+
return self.GYRO_SENS_TABLE.get(fs, 8.75)
69+
70+
def set_gyro_scale(self, dps_val):
71+
fs_map = {250:0, 500:1, 1000:2, 2000:3}
72+
fs = fs_map.get(dps_val, 0)
73+
val = self._read_reg(self.CTRL2_G)
74+
val = (val & ~(0x0C)) | (fs << 2)
75+
self._write_reg(self.CTRL2_G, val)
76+
77+
def read(self):
78+
status = self._read_reg(self.STATUS_REG)
79+
updated = 0
80+
if (status & 0x01) and (status & 0x02):
81+
acc_bytes = self._read_reg(self.ACC_REG, 6)
82+
gyro_bytes = self._read_reg(self.GYRO_REG, 6)
83+
acc_raw = np.array(struct.unpack('<hhh', bytes(acc_bytes)), dtype=np.int16)
84+
gyro_raw = np.array(struct.unpack('<hhh', bytes(gyro_bytes)), dtype=np.int16)
85+
acc_scale = self.get_acc_scale()
86+
gyro_scale = self.get_gyro_scale()
87+
acc_val = acc_raw * acc_scale * self.GRAVITY / 1000 # m/s²
88+
gyro_val = gyro_raw * gyro_scale / 1000 # °/s
89+
self._last_frame = np.array([acc_val, gyro_val])
90+
updated = 1
91+
else:
92+
# No new data, return last
93+
updated = 0
94+
return updated, self._last_frame.copy()
95+
96+
def read_raw(self):
97+
acc_bytes = self._read_reg(self.ACC_REG, 6)
98+
gyro_bytes = self._read_reg(self.GYRO_REG, 6)
99+
acc_raw = np.array(struct.unpack('<hhh', bytes(acc_bytes)), dtype=np.int16)
100+
gyro_raw = np.array(struct.unpack('<hhh', bytes(gyro_bytes)), dtype=np.int16)
101+
return np.array([acc_raw, gyro_raw])
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
from maix import display, app, image
2+
disp = display.Display()
3+
print("display init done")
4+
print(f"display size: {disp.width()}x{disp.height()}")
5+
6+
######################################
7+
import cv2
8+
import numpy as np
9+
# 画布大小
10+
w, h = disp.width(), disp.height()
11+
# 坐标中心
12+
center = np.array([w//2, h//2])
13+
14+
# 构造旋转矩阵: 把原始Z轴(0,0,1)转到acc_norm方向
15+
def rotation_matrix_from_vectors(a, b):
16+
a = a / np.linalg.norm(a)
17+
b = b / np.linalg.norm(b)
18+
v = np.cross(a, b)
19+
c = np.dot(a, b)
20+
s = np.linalg.norm(v)
21+
if s < 1e-8:
22+
return np.eye(3)
23+
kmat = np.array([[ 0, -v[2], v[1]],
24+
[ v[2], 0, -v[0]],
25+
[-v[1], v[0], 0]])
26+
return np.eye(3) + kmat + kmat @ kmat * ((1-c)/(s**2))
27+
28+
# 原始坐标轴单位向量
29+
axes = {
30+
'X': np.array([1,0,0]),
31+
'Y': np.array([0,1,0]),
32+
'Z': np.array([0,0,1])
33+
}
34+
colors = {'X': (0,0,255), 'Y': (0,255,0), 'Z': (255,0,0)}
35+
36+
# 投影到画布(简单正交投影,忽略Z)
37+
def project(vec):
38+
scale = 150
39+
x = int(center[0] + vec[0]*scale)
40+
y = int(center[1] - vec[1]*scale)
41+
return (x, y)
42+
######################################
43+
44+
import time
45+
import lsm6dsow
46+
imu = lsm6dsow.Lsm6dsow()
47+
imu.set_acc_odr(imu.ODR_TABLE[7])
48+
imu.set_gyro_odr(imu.ODR_TABLE[7])
49+
imu.set_acc_scale(imu.ACC_SENS_TABLE[0])
50+
imu.set_gyro_scale(imu.GYRO_SENS_TABLE[0])
51+
52+
count = 0
53+
duration = 180.0 # 统计180秒
54+
start = time.time()
55+
while not app.need_exit():
56+
# 新建画布
57+
canvas = np.ones((h, w, 3), dtype=np.uint8) * 255
58+
59+
remain_time = start + duration - time.time()
60+
if remain_time < 0:
61+
break
62+
else:
63+
cv2.putText(canvas, f"Auto exit after {remain_time:.1f}s", (20,30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255,0,0), 2)
64+
65+
# 同时采样加速度和陀螺仪
66+
upd, acc_gyro_vals = imu.read()
67+
if upd:
68+
# print("ACC(m/s²):", " ".join("{:8.4f}".format(x) for x in acc_vals),
69+
# "GYRO(°/s):", " ".join("{:8.4f}".format(x) for x in gyro_vals))
70+
count += 1
71+
acc_vals = acc_gyro_vals[0]
72+
gyro_vals = acc_gyro_vals[1]
73+
74+
# 1. 示例加速度(m/s²)和角速度(°/s),请替换为你的实际数据
75+
acc = np.array(acc_vals) # X, Y, Z
76+
gyro = np.array(gyro_vals) # X, Y, Z
77+
78+
# 2. 归一化加速度作为“新Z轴方向”
79+
acc_norm = acc / np.linalg.norm(acc) if np.linalg.norm(acc) > 1e-5 else np.array([0,0,1])
80+
81+
# 3. 计算旋转后的坐标轴
82+
R = rotation_matrix_from_vectors(np.array([0,0,1]), acc_norm)
83+
rot_axes = {k: R @ v for k,v in axes.items()}
84+
85+
# 4. 画三轴
86+
for k in ['X', 'Y', 'Z']:
87+
tip = project(rot_axes[k])
88+
cv2.arrowedLine(canvas, tuple(center), tip, colors[k], 3, tipLength=0.10)
89+
cv2.putText(canvas, k, tip, cv2.FONT_HERSHEY_SIMPLEX, 0.7, colors[k], 2)
90+
91+
# 5. 角速度箭头可视化(方向和长度)
92+
gyro_body = gyro / (np.linalg.norm(gyro)+1e-8) if np.linalg.norm(gyro)>1e-8 else np.array([0,0,1])
93+
gyro_len = min(float(np.linalg.norm(gyro)), 1.0) * 100 # 可调整箭头长度比例
94+
# 6. 旋转到加速度系
95+
gyro_proj = R @ gyro_body
96+
gyro_tip = project(gyro_proj * (gyro_len/150)) # 按长度比例缩放
97+
98+
cv2.arrowedLine(canvas, tuple(center), gyro_tip, (0,0,0), 4, tipLength=0.15)
99+
cv2.putText(canvas, "gyro", gyro_tip, cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,0,0), 2)
100+
101+
# 7. 显示数值
102+
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)
103+
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)
104+
105+
fps = count / (time.time() - start)
106+
cv2.putText(canvas, f"fps: {fps:.2f}", (20,120), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,255,0), 2)
107+
108+
img = image.cv2image(canvas)
109+
disp.show(img)
110+
111+
rate = count / duration
112+
print(f"有效采集速率: {rate:.2f} Hz")
113+

examples/ext_dev/sensors/tiny1c/tiny1c_example.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,10 @@ def scale_to_range(arr, target_min, target_max):
252252

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

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

261261
if enable_x3:
Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,49 @@
1-
from maix import display, app, time, ext_dev
1+
from maix import display, image, app, time, ext_dev
2+
3+
from maix import pinmap
4+
5+
import numpy as np
6+
import cv2
7+
8+
pin_function = {
9+
"A8": "I2C7_SCL",
10+
"A9": "I2C7_SDA",
11+
"B21": "SPI2_CS1",
12+
"B19": "SPI2_MISO",
13+
"B18": "SPI2_MOSI",
14+
"B20": "SPI2_SCK"
15+
}
16+
17+
for pin, func in pin_function.items():
18+
if 0 != pinmap.set_pin_function(pin, func):
19+
print(f"Failed: pin{pin}, func{func}")
20+
exit(-1)
221

322
disp = display.Display()
423

524
tof = ext_dev.tof100.Tof100(
6-
4,
7-
ext_dev.tof100.Resolution.RES_50x50,
25+
2,
26+
ext_dev.tof100.Resolution.RES_100x100,
827
ext_dev.cmap.Cmap.JET,
928
40, 1000)
1029

30+
t0 = time.time()
1131
while not app.need_exit():
1232
img = tof.image()
1333
if img is not None:
14-
disp.show(img)
15-
print("min: ", tof.min_dis_point())
16-
print("max: ", tof.max_dis_point())
17-
print("center: ", tof.center_point())
34+
img_bgr = image.image2cv(img, ensure_bgr=True, copy=True)
35+
img_bgr = cv2.rotate(img_bgr, cv2.ROTATE_180)
36+
img_bgr = cv2.resize(img_bgr, (400, 400))
37+
38+
scr = np.zeros((disp.height(), disp.width(), 3), dtype=np.uint8)
39+
scr[:img_bgr.shape[0], :img_bgr.shape[1], ...] = img_bgr
40+
disp.show(image.cv2image(scr))
1841
fps = time.fps()
19-
print(f"time: {1000/fps:.02f}ms, fps: {fps:.02f}")
42+
t1 = time.time()
43+
if t1-t0>1:
44+
print("min: ", tof.min_dis_point())
45+
print("max: ", tof.max_dis_point())
46+
print("center: ", tof.center_point())
47+
print(f"time: {1000/fps:.02f}ms, fps: {fps:.02f}")
48+
print(f"t0:{t0}, t1:{t1}")
49+
t0=t1
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
id: thermal_camera
2+
name: Thermal Camera
3+
name[zh]: 热成像仪
4+
version: 1.0.0
5+
icon: assets/thermal.json
6+
author: Sipeed Ltd
7+
desc: Thermal Camera
8+
desc[zh]: 热成像仪
9+
files:
10+
- assets/thermal.json
11+
- app.yaml
12+
- main.py

projects/app_thermal_camera/assets/thermal.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)