diff --git a/docs/hardware/zh/metasense/assets/tof-1.12.jpg b/docs/hardware/zh/metasense/assets/tof-1.12.jpg
new file mode 100644
index 00000000..a20dd630
Binary files /dev/null and b/docs/hardware/zh/metasense/assets/tof-1.12.jpg differ
diff --git a/docs/hardware/zh/metasense/assets/tof-1.13.jpg b/docs/hardware/zh/metasense/assets/tof-1.13.jpg
new file mode 100644
index 00000000..8fd6e40c
Binary files /dev/null and b/docs/hardware/zh/metasense/assets/tof-1.13.jpg differ
diff --git a/docs/hardware/zh/metasense/assets/tof-1.7.jpg b/docs/hardware/zh/metasense/assets/tof-1.7.jpg
deleted file mode 100644
index 796962e1..00000000
Binary files a/docs/hardware/zh/metasense/assets/tof-1.7.jpg and /dev/null differ
diff --git a/docs/hardware/zh/metasense/assets/tof-1.9.jpg b/docs/hardware/zh/metasense/assets/tof-1.9.jpg
deleted file mode 100644
index c526d409..00000000
Binary files a/docs/hardware/zh/metasense/assets/tof-1.9.jpg and /dev/null differ
diff --git a/docs/hardware/zh/metasense/metasense-a010/assets/a010-13.jpg b/docs/hardware/zh/metasense/metasense-a010/assets/a010-13.jpg
deleted file mode 100644
index 5294deae..00000000
Binary files a/docs/hardware/zh/metasense/metasense-a010/assets/a010-13.jpg and /dev/null differ
diff --git a/docs/hardware/zh/metasense/metasense-a010/assets/a010-14.jpg b/docs/hardware/zh/metasense/metasense-a010/assets/a010-14.jpg
deleted file mode 100644
index 53e1f3f2..00000000
Binary files a/docs/hardware/zh/metasense/metasense-a010/assets/a010-14.jpg and /dev/null differ
diff --git a/docs/hardware/zh/metasense/metasense-a010/assets/a010-6.jpg b/docs/hardware/zh/metasense/metasense-a010/assets/a010-6.jpg
deleted file mode 100644
index a8ce8441..00000000
Binary files a/docs/hardware/zh/metasense/metasense-a010/assets/a010-6.jpg and /dev/null differ
diff --git a/docs/hardware/zh/metasense/metasense-a010/assets/a010-7.jpg b/docs/hardware/zh/metasense/metasense-a010/assets/a010-7.jpg
deleted file mode 100644
index e7258f85..00000000
Binary files a/docs/hardware/zh/metasense/metasense-a010/assets/a010-7.jpg and /dev/null differ
diff --git a/docs/hardware/zh/metasense/metasense-a010/assets/a010-10.jpg b/docs/hardware/zh/metasense/metasense-a010/assets/ms-RVIZ.jpg
similarity index 100%
rename from docs/hardware/zh/metasense/metasense-a010/assets/a010-10.jpg
rename to docs/hardware/zh/metasense/metasense-a010/assets/ms-RVIZ.jpg
diff --git a/docs/hardware/zh/metasense/metasense-a010/assets/ms-lamp.jpg b/docs/hardware/zh/metasense/metasense-a010/assets/ms-lamp.jpg
new file mode 100644
index 00000000..d4dfce60
Binary files /dev/null and b/docs/hardware/zh/metasense/metasense-a010/assets/ms-lamp.jpg differ
diff --git a/docs/hardware/zh/metasense/metasense-a010/assets/ms-mcu.jpg b/docs/hardware/zh/metasense/metasense-a010/assets/ms-mcu.jpg
new file mode 100644
index 00000000..3875452a
Binary files /dev/null and b/docs/hardware/zh/metasense/metasense-a010/assets/ms-mcu.jpg differ
diff --git a/docs/hardware/zh/metasense/metasense-a010/assets/ms-people.jpg b/docs/hardware/zh/metasense/metasense-a010/assets/ms-people.jpg
new file mode 100644
index 00000000..4e8f35f7
Binary files /dev/null and b/docs/hardware/zh/metasense/metasense-a010/assets/ms-people.jpg differ
diff --git a/docs/hardware/zh/metasense/metasense-a010/assets/a010-8.jpg b/docs/hardware/zh/metasense/metasense-a010/assets/ms-ros.jpg
similarity index 100%
rename from docs/hardware/zh/metasense/metasense-a010/assets/a010-8.jpg
rename to docs/hardware/zh/metasense/metasense-a010/assets/ms-ros.jpg
diff --git a/docs/hardware/zh/metasense/metasense-a010/assets/a010-9.jpg b/docs/hardware/zh/metasense/metasense-a010/assets/ms-rqt.jpg
similarity index 100%
rename from docs/hardware/zh/metasense/metasense-a010/assets/a010-9.jpg
rename to docs/hardware/zh/metasense/metasense-a010/assets/ms-rqt.jpg
diff --git a/docs/hardware/zh/metasense/metasense-a010/at_command.md b/docs/hardware/zh/metasense/metasense-a010/at_command.md
new file mode 100644
index 00000000..68350b08
--- /dev/null
+++ b/docs/hardware/zh/metasense/metasense-a010/at_command.md
@@ -0,0 +1,169 @@
+# MetaSense-A010 二次开发手册
+
+## AT 指令表
+
+| AT | |
+| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| +ISP
Image Signal Processor | :0: turn ISP off
=1: turn ISP on |
+| +BINN
full binning | =1: output 100x100 pixel frame
=2: output 50x50 pixel frame
=4: output 25x25 pixel frame
|
+| +DISP
display mux | =0: all off
=1: lcd display on
=2: usb display on
=3: lcd and usb display on
=4: uart display on
=5: lcd and uart display on
=6: usb and uart display on
=7: lcd, usb and uart display on |
+| +BAUD
uart baudrate | =0: 9600
=1: 57600
=2: 115200
=3: 230400
=4: 460800
=5: 921600
=6: 1000000
=7: 2000000
=8: 3000000 |
+| +UNIT
quantization unit | =0: auto
=1-10: quantizated by unit(mm) |
+| +FPS
frame per second | =1-19: set frame per second |
+| +Save
save config | : save current configuration |
+
+
+|输入|执行|注释|
+|---|---|---|
+|AT+ISP? |\r|返回当前ISP状态|
+|AT+ISP=? |\r|返回所有支持的ISP状态|
+|AT+ISP=< MODE >|\r|选择ISP状态|
+
+参数:
+|< MODE > | 含义 |
+|----|----|
+|0 "STOP ISP" |立即关闭模组ISP,停止IR发射器|
+|1 "LAUNCH ISP" |计划启动模组ISP,实际出图需等待1~2秒|
+
+BINN指令
+句法:
+| 输入 | 执行 | 注释 |
+|----|----|----|
+| AT+BINN? | \r | 返回当前BINN状态 |
+| AT+BINN=? | \r | 返回所有支持的BINN状态 |
+| AT+BINN= < MODE > | \r | 选择BINN状态 |
+
+参数:
+| < MODE > | 含义 |
+|----------|------|
+| 1 "1x1 BINN" | 1x1相当于无binning,实际出图分辨率为100x100。 |
+| 2 "2x2 BINN" | 2×2binning,4个像素点合并成1个,实际出图分辨率为50×50计划启动模组ISP,实际出图需等待1~2秒。|
+| 4 "4x4 BINN" | 4×4binning,16个像素点合并成1个,实际出图分辨率为25×25。 |
+
+
+DISP指令
+请按需开启,避免资源过度占用
+句法:
+
+| 输入 | 执行 | 注释 |
+|------|------|-----|
+| AT+DISP? | \r | 返回当前DISP状态 |
+| AT+DISP=? | \r | 返回所有支持的DISP状态 |
+| AT+DISP=< MODE > | \r | 选择DISP状态 |
+
+参数:
+
+
+| < MODE > | 含义 |
+|----------|------|
+| 0 | all off |
+| 1 | lcd display on |
+| 2 | usb display on |
+| 3 | lcd and usb display on |
+| 4 | uart display on |
+| 5 | lcd and uart display on |
+| 6 | usb and uart display on |
+| 7 | lcd, usb and uart display on |
+
+BAUD指令
+句法:
+
+| 输入 | 执行 | 注释 |
+|------|-----|------|
+| AT+BAUD? | \r | 返回当前BAUD状态 |
+| AT+BAUD=? | \r | 返回所有支持的BAUD状态 |
+| AT+BAUD=< MODE > | \r | 选择BAUD状态 |
+
+参数:
+
+| < MODE > | 含义 |
+|----------|------|
+| 0 | 9600 |
+| 1 | 57600 |
+| 2 | 115200 |
+| 3 | 230400 |
+| 4 | 460800 |
+| 5 | 921600 |
+| 6 | 1000000 |
+| 7 | 2000000 |
+| 8 | 3000000 |
+
+UNIT指令
+句法:
+
+| 输入 | 执行 | 注释 |
+|------|------|------|
+| AT+UNIT? | \r | 返回当前UNIT值 |
+| AT+UNIT=? | \r | 返回所有支持的UNIT值 |
+| AT+UNIT=< UINT > | \r | 选择UNIT值 |
+
+参数:
+| < UINT > | 含义 |
+|----------|------|
+| 0 "DEFAULT UNIT" | 采用默认量化策略,因tof特性导致成像近处精度优于远距离处,故放大近距离处差异,采用5.1*sqrt(x)将16bit的原数据量化为8bit
+1...9 "QUANTIZE UNIT"s
+
+代表以x mm为单位进行量化,取值越小细节越多,同时可视距离越短,请合理设置
+FPS指令
+句法:
+输入
+执行
+注释
+AT+FPS?
+\r
+返回当前FPS值
+AT+FPS=?
+\r
+返回所有支持的FPS值
+AT+FPS=
+\r
+选择FPS值
+参数:
+< FPS >
+含义
+1...19 "frame per second"
+
+tof出图帧率,越大越流畅
+SAVE指令
+句法:
+输入
+执行
+注释
+AT+SAVE
+\r
+固化TOF摄像头当前配置,事后需要复位
+
+多机 和 AE 指令建议加入
+ANTIMMI指令
+句法:
+输入
+执行
+注释
+AT+ANTIMMI?
+\r
+返回当前ANTIMMI状态
+AT+ANTIMMI=?
+\r
+返回所有支持的ANTIMMI状态
+AT+ANTIMMI=
+\r
+选择ANTIMMI状态
+参数:
+
+含义
+-1
+disable anti-mmi
+0
+auto anti-mmi
+1-41
+manual anti-mmi usb display on
+
+图像数据包说明
+上电默认启动ISP并在显示屏显示图像,同时输出图像数据到uart和usb
+图像数据封装成包(未稳定):
+1. 包头2字节:0X00、0XFF
+2. 包长度2字节:当前包剩余数据的字节数
+3. 其他内容16字节:包括包序号、包长度、分辨率等等
+4. 图像帧
+5. 校验1字节:之前所有字节的“和”低八位
+6. 包尾1字节:0XDD
\ No newline at end of file
diff --git a/docs/hardware/zh/metasense/metasense-a010/code.md b/docs/hardware/zh/metasense/metasense-a010/code.md
new file mode 100644
index 00000000..b9692bc7
--- /dev/null
+++ b/docs/hardware/zh/metasense/metasense-a010/code.md
@@ -0,0 +1,921 @@
+# 源码总览
+
+- [源码总览](#源码总览)
+ - [tof_mainpy](#tof_mainpy)
+ - [streampy](#streampy)
+ - [calvolumespy](#calvolumespy)
+ - [calibratepy](#calibratepy)
+
+## tof_mainpy
+
+```python
+from fpioa_manager import fm
+from machine import UART
+import lcd, image
+
+# lcd.init(invert=True)
+lcd.init()
+img = image.Image()
+
+fm.register(24, fm.fpioa.UART1_TX, force=True)
+fm.register(25, fm.fpioa.UART1_RX, force=True)
+
+uart_A = UART(UART.UART1, 115200, 8, 0, 0, timeout=1000, read_buf_len=4096)
+
+def uart_readBytes():
+ return uart_A.read()
+
+
+def uart_hasData():
+ return uart_A.any()
+
+
+def uart_sendCmd(cmd):
+ uart_A.write(cmd)
+
+uart_sendCmd(b"AT+BAUD=5\r")
+
+uart_A.deinit()
+
+uart_A = UART(UART.UART1, 921600, 8, 0, 0, timeout=1000, read_buf_len=4096)
+
+jetcolors = [
+ (128, 0, 0), (132, 0, 0), (136, 0, 0), (140, 0, 0), (144, 0, 0), (148, 0, 0), (152, 0, 0), (156, 0, 0), (160, 0, 0), (164, 0, 0), (168, 0, 0), (172, 0, 0), (176, 0, 0), (180, 0, 0), (184, 0, 0), (188, 0, 0), (192, 0, 0), (196, 0, 0), (200, 0, 0), (204, 0, 0), (208, 0, 0), (212, 0, 0), (216, 0, 0), (220, 0, 0), (224, 0, 0), (228, 0, 0), (232, 0, 0), (236, 0, 0), (240, 0, 0), (244, 0, 0), (248, 0, 0), (252, 0, 0), (255, 0, 0), (255, 4, 0), (255, 8, 0), (255, 12, 0), (255, 16, 0), (255, 20, 0), (255, 24, 0), (255, 28, 0), (255, 32, 0), (255, 36, 0), (255, 40, 0), (255, 44, 0), (255, 48, 0), (255, 52, 0), (255, 56, 0), (255, 60, 0), (255, 64, 0), (255, 68, 0), (255, 72, 0), (255, 76, 0), (255, 80, 0), (255, 84, 0), (255, 88, 0), (255, 92, 0), (255, 96, 0), (255, 100, 0), (255, 104, 0), (255, 108, 0), (255, 112, 0), (255, 116, 0), (255, 120, 0), (255, 124, 0), (255, 128, 0), (255, 132, 0), (255, 136, 0), (255, 140, 0), (255, 144, 0), (255, 148, 0), (255, 152, 0), (255, 156, 0), (255, 160, 0), (255, 164, 0), (255, 168, 0), (255, 172, 0), (255, 176, 0), (255, 180, 0), (255, 184, 0), (255, 188, 0), (255, 192, 0), (255, 196, 0), (255, 200, 0), (255, 204, 0), (255, 208, 0), (255, 212, 0), (255, 216, 0), (255, 220, 0), (255, 224, 0), (255, 228, 0), (255, 232, 0), (255, 236, 0), (255, 240, 0), (255, 244, 0), (255, 248, 0), (255, 252, 0), (254, 255, 1), (250, 255, 6), (246, 255, 10), (242, 255, 14), (238, 255, 18), (234, 255, 22), (230, 255, 26), (226, 255, 30), (222, 255, 34), (218, 255, 38), (214, 255, 42), (210, 255, 46), (206, 255, 50), (202, 255, 54), (198, 255, 58), (194, 255, 62), (190, 255, 66), (186, 255, 70), (182, 255, 74), (178, 255, 78), (174, 255, 82), (170, 255, 86), (166, 255, 90), (162, 255, 94), (158, 255, 98), (154, 255, 102), (150, 255, 106), (146, 255, 110), (142, 255, 114), (138, 255, 118), (134, 255, 122), (130, 255, 126),
+ (126, 255, 130), (122, 255, 134), (118, 255, 138), (114, 255, 142), (110, 255, 146), (106, 255, 150), (102, 255, 154), (98, 255, 158), (94, 255, 162), (90, 255, 166), (86, 255, 170), (82, 255, 174), (78, 255, 178), (74, 255, 182), (70, 255, 186), (66, 255, 190), (62, 255, 194), (58, 255, 198), (54, 255, 202), (50, 255, 206), (46, 255, 210), (42, 255, 214), (38, 255, 218), (34, 255, 222), (30, 255, 226), (26, 255, 230), (22, 255, 234), (18, 255, 238), (14, 255, 242), (10, 255, 246), (6, 255, 250), (2, 255, 254), (0, 252, 255), (0, 248, 255), (0, 244, 255), (0, 240, 255), (0, 236, 255), (0, 232, 255), (0, 228, 255), (0, 224, 255), (0, 220, 255), (0, 216, 255), (0, 212, 255), (0, 208, 255), (0, 204, 255), (0, 200, 255), (0, 196, 255), (0, 192, 255), (0, 188, 255), (0, 184, 255), (0, 180, 255), (0, 176, 255), (0, 172, 255), (0, 168, 255), (0, 164, 255), (0, 160, 255), (0, 156, 255), (0, 152, 255), (0, 148, 255), (0, 144, 255), (0, 140, 255), (0, 136, 255), (0, 132, 255), (0, 128, 255), (0, 124, 255), (0, 120, 255), (0, 116, 255), (0, 112, 255), (0, 108, 255), (0, 104, 255), (0, 100, 255), (0, 96, 255), (0, 92, 255), (0, 88, 255), (0, 84, 255), (0, 80, 255), (0, 76, 255), (0, 72, 255), (0, 68, 255), (0, 64, 255), (0, 60, 255), (0, 56, 255), (0, 52, 255), (0, 48, 255), (0, 44, 255), (0, 40, 255), (0, 36, 255), (0, 32, 255), (0, 28, 255), (0, 24, 255), (0, 20, 255), (0, 16, 255), (0, 12, 255), (0, 8, 255), (0, 4, 255), (0, 0, 255), (0, 0, 252), (0, 0, 248), (0, 0, 244), (0, 0, 240), (0, 0, 236), (0, 0, 232), (0, 0, 228), (0, 0, 224), (0, 0, 220), (0, 0, 216), (0, 0, 212), (0, 0, 208), (0, 0, 204), (0, 0, 200), (0, 0, 196), (0, 0, 192), (0, 0, 188), (0, 0, 184), (0, 0, 180), (0, 0, 176), (0, 0, 172), (0, 0, 168), (0, 0, 164), (0, 0, 160), (0, 0, 156), (0, 0, 152), (0, 0, 148), (0, 0, 144), (0, 0, 140), (0, 0, 136), (0, 0, 132), (0, 0, 128)
+]
+
+def show(frameData, res):
+ resR = res[0]
+ resC = res[1]
+ for y in range(resR):
+ for x in range(resC):
+ pixel_cmap_rgb = jetcolors[frameData[y*resR + x]]
+ img.set_pixel(110 + x, 70 + y, pixel_cmap_rgb)
+ lcd.display(img)
+ img.clear()
+
+FRAME_HEAD = b"\x00\xFF"
+FRAME_TAIL = b"\xCC"
+
+from struct import unpack
+# send_cmd("AT+BINN=2\r")
+uart_sendCmd(b"AT+DISP=5\r")
+uart_sendCmd(b"AT+FPS=10\r")
+
+# while True:
+# if uart_hasData():
+# print(uart_readBytes())
+
+rawData = b''
+while True:
+ if not uart_hasData():
+ continue
+ rawData += uart_readBytes()
+ idx = rawData.find(FRAME_HEAD)
+ if idx < 0:
+ continue
+ rawData = rawData[idx:]
+ # print(rawData)
+ # check data length 2Byte
+ dataLen = unpack("H", rawData[2: 4])[0]
+ # print("len: "+str(dataLen))
+ frameLen = len(FRAME_HEAD) + 2 + dataLen + 2
+ frameDataLen = dataLen - 16
+
+ if len(rawData) < frameLen:
+ continue
+ # get data
+ frame = rawData[:frameLen]
+ # print(frame.hex())
+ rawData = rawData[frameLen:]
+
+ frameTail = frame[-1]
+ # print("tail: "+str(hex(frameTail)))
+ _sum = frame[-2]
+ # print("checksum: "+str(hex(_sum)))
+ # check sum
+ # spi has no checksum but i add one
+ if frameTail != 0xdd and _sum != sum(frame[:frameLen - 2]) % 256:
+ continue
+
+ frameID = unpack("H", frame[16:18])[0]
+ # print("frame ID: "+str(frameID))
+
+ resR = unpack("B", frame[14:15])[0]
+ resC = unpack("B", frame[15:16])[0]
+ res = (resR, resC)
+ # print(res)
+ # frameData=[ unpack("H", frame[20+i:22+i])[0] for i in range(0, frameDataLen, 2) ]
+ frameData = [unpack("B", frame[20+i:21+i])[0]
+ for i in range(0, frameDataLen, 1)]
+
+ show(frameData, res)
+
+ del frameData
+```
+
+## streampy
+
+```python
+from PIL import Image
+import requests
+import matplotlib.pyplot as plt
+import struct
+import numpy as np
+import cv2
+
+
+def frame_config_decode(frame_config):
+ '''
+ @frame_config bytes
+
+ @return fields, tuple (trigger_mode, deep_mode, deep_shift, ir_mode, status_mode, status_mask, rgb_mode, rgb_res, expose_time)
+ '''
+ return struct.unpack("> with_config[1]
+ deepth_img = struct.unpack("<%us" % deepth_size, frame_payload[:deepth_size])[
+ 0] if 0 != deepth_size else None
+ frame_payload = frame_payload[deepth_size:]
+
+ # 0:16bit 1:8bit, resolution: 320*240
+ ir_size = (320*240*2) >> with_config[3]
+ ir_img = struct.unpack("<%us" % ir_size, frame_payload[:ir_size])[
+ 0] if 0 != ir_size else None
+ frame_payload = frame_payload[ir_size:]
+
+ status_size = (320*240//8) * (16 if 0 == with_config[4] else
+ 2 if 1 == with_config[4] else 8 if 2 == with_config[4] else 1)
+ status_img = struct.unpack("<%us" % status_size, frame_payload[:status_size])[
+ 0] if 0 != status_size else None
+ frame_payload = frame_payload[status_size:]
+
+ assert(deep_data_size == deepth_size+ir_size+status_size)
+
+ rgb_size = len(frame_payload)
+ assert(rgb_data_size == rgb_size)
+ rgb_img = struct.unpack("<%us" % rgb_size, frame_payload[:rgb_size])[
+ 0] if 0 != rgb_size else None
+
+ if (not rgb_img is None) and (1 == with_config[6]):
+ jpeg = cv2.imdecode(np.frombuffer(
+ rgb_img, 'uint8', rgb_size), cv2.IMREAD_COLOR)
+ if not jpeg is None:
+ rgb = cv2.cvtColor(jpeg, cv2.COLOR_BGR2RGB)
+ rgb_img = rgb.tobytes()
+ else:
+ rgb_img = None
+
+ return (deepth_img, ir_img, status_img, rgb_img)
+
+
+HOST = '192.168.233.1'
+PORT = 80
+
+
+def post_encode_config(config=frame_config_encode(), host=HOST, port=PORT):
+ r = requests.post('http://{}:{}/set_cfg'.format(host, port), config)
+ if(r.status_code == requests.codes.ok):
+ return True
+ return False
+
+
+def get_frame_from_http(host=HOST, port=PORT):
+ r = requests.get('http://{}:{}/getdeep'.format(host, port))
+ if(r.status_code == requests.codes.ok):
+ # print('Get deep image')
+ deepimg = r.content
+ # print('Length={}'.format(len(deepimg)))
+ (frameid, stamp_msec) = struct.unpack(' 65535)] = 65535
+
+ # depth = depth_f.astype(depth.dtype)
+
+ # depth[240//2, 320//2 - 5:320//2+5] = 0x00
+ # depth[240//2-5:240//2+5, 320//2] = 0x00
+ ax1.imshow(depth, cmap='jet_r')
+ ax2 = fig.add_subplot(222)
+ if not ir is None:
+ ax2.imshow(ir, cmap='gray')
+ ax3 = fig.add_subplot(223)
+ if not status is None:
+ ax3.imshow(status)
+ ax4 = fig.add_subplot(224)
+ if not rgb is None:
+ ax4.imshow(rgb)
+
+
+if post_encode_config(frame_config_encode(1, 1, 255, 0, 2, 7, 1, 0, 0)):
+ # 打开交互模式
+ plt.ion()
+ figsize = (12, 12)
+ fig = plt.figure('2D frame', figsize=figsize)
+ while True:
+ p = get_frame_from_http()
+ show_frame(fig, p)
+ # 停顿时间
+ plt.pause(0.001)
+ # 清除当前画布
+ fig.clf()
+
+ plt.ioff()
+```
+
+## calvolumespy
+
+```python
+from PIL import Image, ImageDraw
+import requests
+import matplotlib.pyplot as plt
+import struct
+import numpy as np
+import cv2
+
+HOST = '192.168.233.1'
+PORT = 80
+
+def depth2xyz(xp, yp, z, fx, fy, cx, cy, depth_scale=1000):
+ # h,w=np.mgrid[0:depth_map.shape[0],0:depth_map.shape[1]]
+ z = z/depth_scale
+ x = (xp-cx)*z/fx
+ y = (yp-cy)*z/fy
+ # xyz=np.dstack((x,y,z))
+ # xyz=cv2.rgbd.depthTo3d(depth_map,depth_cam_matrix)
+ return [x, y, z]
+
+
+def polygon_area(polygon):
+ area = 0
+ q = polygon[-1]
+ for p in polygon:
+ area += p[0] * q[1] - p[1] * q[0]
+ q = p
+ return abs(area) / 2.0
+
+def get_lenscoeff(host=HOST, port=PORT):
+ r = requests.get('http://{}:{}/getinfo'.format(host, port))
+ if(r.status_code == requests.codes.ok):
+ lenscoeff_bin = r.content
+ (_fx,_fy,_cx,_cy) = struct.unpack(' diff_high, 0, diff1)
+ diff1 = (np.where(diff1 > 0, 1, 0)*255).astype(np.uint8)
+ # plt.imshow(diff1)
+
+ # print(d_bk.shape) (240, 320)
+ output = np.zeros((img_h, img_w, 3), np.uint8)
+
+ num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(
+ diff1, connectivity=8)
+ # print('num_labels = ',num_labels)
+ # 连通域的信息:对应各个轮廓的x、y、width、height和面积
+ # print('stats = ',stats)
+ res = list()
+ max_stats = list()
+ for i in range(5):
+ max_label = 1+np.argmax(stats[1:, 4])
+ # print('stats[max_label] = ', stats[max_label])
+ if i > 0 and stats[max_label][4] < 700:
+ break
+ max_stat = stats[max_label]
+ max_stats.append(max_stat)
+ stats[max_label][4] = 0
+
+ mask = (labels == max_label)
+ # (np.random.rand(3)*255).astype(np.uint8)
+ output[:, :, :][mask] = [200, 0, 0]
+ # plt.imshow(output)
+
+ # kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
+ # eroded = cv2.erode(output, kernel)
+ # dilated = cv2.dilate(output, kernel)
+
+ kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
+ # output = dilated
+ output=cv2.morphologyEx(output, cv2.MORPH_OPEN, kernel)
+ output=cv2.morphologyEx(output, cv2.MORPH_CLOSE, kernel)
+
+
+ volumes = []
+ # points = []
+ # areas = []
+ for yp in range(img_h):
+ for xp in range(img_w):
+ if mask[yp, xp]:
+ x1, y1, z1 = depth2xyz(
+ xp, yp, d_bk[yp, xp], fx, fy, cx, cy, depth_scale=1)
+ x0, y0, z0 = depth2xyz(
+ xp, yp, d_bg[yp, xp], fx, fy, cx, cy, depth_scale=1)
+
+ x = xp-1
+ if x < 0:
+ x = 0
+ y = yp
+ xl, yl, zl = depth2xyz(
+ x, y, d_bk[y, x], fx, fy, cx, cy, depth_scale=1)
+ x = xp+1
+ if x >= img_w:
+ x = img_w-1
+ y = yp
+ xr, yr, zr = depth2xyz(
+ x, y, d_bk[y, x], fx, fy, cx, cy, depth_scale=1)
+ x = xp
+ y = yp-1
+ if y < 0:
+ y = 0
+ xt, yt, zt = depth2xyz(
+ x, y, d_bk[y, x], fx, fy, cx, cy, depth_scale=1)
+ x = xp
+ y = yp+1
+ if y >= img_h:
+ y = img_h-1
+ xb, yb, zb = depth2xyz(
+ x, y, d_bk[y, x], fx, fy, cx, cy, depth_scale=1)
+
+ area_a = polygon_area(
+ [[xt, yt], [xl, yl], [xb, yb], [xr, yr]])/2
+
+ dz = z0-z1
+ dx = z1/fx
+ dy = z1/fy
+ area_b = dx*dy*2/2
+
+ area = (area_a+area_b)/2 # avg get better acc
+ volume = area*dz
+ # areas.append(area)
+ volumes.append(volume)
+ # points.append((x1, y1, dz))
+ # areas = np.array(areas)
+ volumes = np.array(volumes)
+ # points = np.array(points)
+
+ res.append("{}:{} cm3".format(i, int(np.sum(volumes)/1000)))
+ # print(res)
+
+ img_pil = Image.fromarray(output)
+ draw = ImageDraw.Draw(img_pil)
+ for i in range(len(max_stats)):
+ max_stat = max_stats[i]
+ draw.rectangle([(max_stat[0], max_stat[1]),
+ (max_stat[0] + max_stat[2], max_stat[1] + max_stat[3])], outline="red")
+ draw.text((max_stat[0], max_stat[1]), res[i], fill=(255, 255, 255))
+ output = np.array(img_pil)
+
+ return output
+
+
+def frame_config_decode(frame_config):
+ '''
+ @frame_config bytes
+
+ @return fields, tuple (trigger_mode, deep_mode, deep_shift, ir_mode, status_mode, status_mask, rgb_mode, rgb_res, expose_time)
+ '''
+ return struct.unpack("> with_config[1]
+ deepth_img = struct.unpack("<%us" % deepth_size, frame_payload[:deepth_size])[
+ 0] if 0 != deepth_size else None
+ frame_payload = frame_payload[deepth_size:]
+
+ # 0:16bit 1:8bit, resolution: 320*240
+ ir_size = (320*240*2) >> with_config[3]
+ ir_img = struct.unpack("<%us" % ir_size, frame_payload[:ir_size])[
+ 0] if 0 != ir_size else None
+ frame_payload = frame_payload[ir_size:]
+
+ status_size = (320*240//8) * (16 if 0 == with_config[4] else
+ 2 if 1 == with_config[4] else 8 if 2 == with_config[4] else 1)
+ status_img = struct.unpack("<%us" % status_size, frame_payload[:status_size])[
+ 0] if 0 != status_size else None
+ frame_payload = frame_payload[status_size:]
+
+ assert(deep_data_size == deepth_size+ir_size+status_size)
+
+ rgb_size = len(frame_payload)
+ assert(rgb_data_size == rgb_size)
+ rgb_img = struct.unpack("<%us" % rgb_size, frame_payload[:rgb_size])[
+ 0] if 0 != rgb_size else None
+
+ if (not rgb_img is None) and (1 == with_config[6]):
+ jpeg = cv2.imdecode(np.frombuffer(
+ rgb_img, 'uint8', rgb_size), cv2.IMREAD_COLOR)
+ if not jpeg is None:
+ rgb = cv2.cvtColor(jpeg, cv2.COLOR_BGR2RGB)
+ rgb_img = rgb.tobytes()
+ else:
+ rgb_img = None
+
+ return (deepth_img, ir_img, status_img, rgb_img)
+
+
+def post_encode_config(config=frame_config_encode(), host=HOST, port=PORT):
+ r = requests.post('http://{}:{}/set_cfg'.format(host, port), config)
+ if(r.status_code == requests.codes.ok):
+ return True
+ return False
+
+
+def get_frame_from_http(host=HOST, port=PORT):
+ r = requests.get('http://{}:{}/getdeep'.format(host, port))
+ if(r.status_code == requests.codes.ok):
+ # print('Get deep image')
+ deepimg = r.content
+ # print('Length={}'.format(len(deepimg)))
+ (frameid, stamp_msec) = struct.unpack(' 65535)] = 65535
+
+ # depth = depth_f.astype(depth.dtype)
+
+ # depth[240//2, 320//2 - 5:320//2+5] = 0x00
+ # depth[240//2-5:240//2+5, 320//2] = 0x00
+
+ if not UPDATE_BG[1] is None:
+ ax1.imshow(cal_volume(depth, UPDATE_BG[1]))
+ else:
+ ax1.imshow(depth)
+
+ if UPDATE_BG[0]:
+ UPDATE_BG[1] = depth
+
+ # ax2 = fig.add_subplot(222)
+ # if not ir is None:
+ # ax2.imshow(ir, cmap='gray')
+ # ax3 = fig.add_subplot(223)
+ # if not status is None:
+ # ax3.imshow(status)
+ ax4 = fig.add_subplot(121)
+ if not rgb is None:
+ ax4.imshow(rgb)
+
+
+UPDATE_BG = [False, None]
+
+if post_encode_config(frame_config_encode(1, 0, 255, 0, 2, 7, 1, 0, 0)):
+ # 打开交互模式
+ def on_key_press(event):
+ if event.key == ' ':
+ UPDATE_BG[0] = True
+ elif event.key == 'c':
+ UPDATE_BG[1] = None
+
+ plt.ion()
+ figsize = (12, 12)
+ fig = plt.figure('2D frame', figsize=figsize)
+ fig.canvas.mpl_connect('key_press_event', on_key_press)
+
+ print("按下空格键更新背景图,按下c键清空背景图")
+ while True:
+ p = get_frame_from_http()
+ show_frame(fig, p)
+ if UPDATE_BG[0]:
+ UPDATE_BG[0] = False
+ print("update bg success!")
+ # 停顿时间
+ plt.pause(0.001)
+ # 清除当前画布
+ fig.clf()
+
+ plt.ioff()
+```
+
+## calibratepy
+
+```python
+# -*- coding:utf-8 -*-
+
+import requests
+import struct
+import shutil
+import logging
+
+
+from PIL import Image
+import matplotlib.pyplot as plt
+import numpy as np
+import cv2
+import os
+import json
+
+# 默认的warning级别,只输出warning以上的
+# 使用basicConfig()来指定日志级别和相关信息
+
+logging.basicConfig(level=logging.INFO # 设置日志输出格式
+ , filename="cali.log" # log日志输出的文件位置和文件名
+ , filemode="w" # 文件的写入格式,w为重新写入文件,默认是追加
+ # 日志输出的格式
+ # -8表示占位符,让输出左对齐,输出长度都为8位
+ # 时间输出的格式
+ , format="%(asctime)s - %(name)s - %(levelname)-9s - %(filename)-8s : %(lineno)s line - %(message)s", datefmt="%Y-%m-%d %H:%M:%S"
+ )
+
+logging.info("Starting Cali...")
+
+HOST = '192.168.233.233'
+PORT = 80
+
+rgb_dir = 'RGB/'
+tof_dir = 'TOF/'
+
+
+logging.info("Clear {} and {} dir...".format(rgb_dir, tof_dir))
+
+if os.path.exists(rgb_dir):
+ shutil.rmtree(rgb_dir)
+
+if os.path.exists(tof_dir):
+ shutil.rmtree(tof_dir)
+
+os.makedirs(rgb_dir)
+os.makedirs(tof_dir)
+
+logging.info("Clear directories success!")
+
+IMAGECOUNT = 0
+
+
+IMG_WIDTH = 800 # Do not change this
+IMG_HEIGHT = 600 # and this
+patternSize = (8, 6) # modifly this for your Chessboard Corners
+squareSize = 28.3 # modifly this for chessboard box size (in mm)
+
+
+def frame_config_decode(frame_config):
+ '''
+ @frame_config bytes
+
+ @return fields, tuple (trigger_mode, deep_mode, deep_shift, ir_mode, status_mode, status_mask, rgb_mode, rgb_res, expose_time)
+ '''
+ return struct.unpack("> with_config[1]
+ deepth_img = struct.unpack("<%us" % deepth_size, frame_payload[:deepth_size])[
+ 0] if 0 != deepth_size else None
+ frame_payload = frame_payload[deepth_size:]
+
+ # 0:16bit 1:8bit, resolution: 320*240
+ ir_size = (320*240*2) >> with_config[3]
+ ir_img = struct.unpack("<%us" % ir_size, frame_payload[:ir_size])[
+ 0] if 0 != ir_size else None
+ frame_payload = frame_payload[ir_size:]
+
+ status_size = (320*240//8) * (16 if 0 == with_config[4] else
+ 2 if 1 == with_config[4] else 8 if 2 == with_config[4] else 1)
+ status_img = struct.unpack("<%us" % status_size, frame_payload[:status_size])[
+ 0] if 0 != status_size else None
+ frame_payload = frame_payload[status_size:]
+
+ assert(deep_data_size == deepth_size+ir_size+status_size)
+
+ rgb_size = len(frame_payload)
+ assert(rgb_data_size == rgb_size)
+ rgb_img = struct.unpack("<%us" % rgb_size, frame_payload[:rgb_size])[
+ 0] if 0 != rgb_size else None
+
+ if (not rgb_img is None) and (1 == with_config[6]):
+ jpeg = cv2.imdecode(np.frombuffer(
+ rgb_img, 'uint8', rgb_size), cv2.IMREAD_COLOR)
+ if not jpeg is None:
+ rgb = cv2.cvtColor(jpeg, cv2.COLOR_BGR2RGB)
+ rgb_img = rgb.tobytes()
+ else:
+ rgb_img = None
+
+ return (deepth_img, ir_img, status_img, rgb_img)
+
+
+def post_encode_config(config=frame_config_encode(), host=HOST, port=PORT):
+ r = requests.post('http://{}:{}/set_cfg'.format(host, port), config)
+ if(r.status_code == requests.codes.ok):
+ return True
+ return False
+
+
+def get_frame_from_http(host=HOST, port=PORT):
+ r = requests.get('http://{}:{}/getdeep'.format(host, port))
+ if(r.status_code == requests.codes.ok):
+ # print('Get deep image')
+ deepimg = r.content
+ # print('Length={}'.format(len(deepimg)))
+ (frameid, stamp_msec) = struct.unpack(' 65535)] = 65535
+
+ # depth = depth_f.astype(depth.dtype)
+
+ # depth[240//2, 320//2 - 5:320//2+5] = 0x00
+ # depth[240//2-5:240//2+5, 320//2] = 0x00
+ ax1.imshow(depth, cmap='jet_r')
+ ax2 = fig.add_subplot(222)
+ if not ir is None:
+ ax2.imshow(ir, cmap='gray')
+ if save_flag:
+ Image.fromarray(ir).resize((800, 600)).save(
+ tof_dir+'IR-{}.png'.format(IMAGECOUNT))
+ ax3 = fig.add_subplot(223)
+ if not status is None:
+ ax3.imshow(status)
+ ax4 = fig.add_subplot(224)
+ if not rgb is None:
+ ax4.imshow(rgb)
+ if save_flag:
+ Image.fromarray(rgb).resize((800, 600)).save(
+ rgb_dir+'RGB-{}.png'.format(IMAGECOUNT))
+
+
+def main():
+ global IMAGECOUNT
+ if post_encode_config(frame_config_encode(1, 1, 255, 0, 2, 7, 1, 0, 0)):
+ KEEP_STREAM = [True]
+ save_once = [False]
+
+ def on_key_press(event):
+ if event.key == ' ':
+ save_once[0] = True
+ elif event.key == 'y':
+ KEEP_STREAM[0] = False
+
+ # 打开交互模式
+ plt.ion()
+ figsize = (12, 12)
+ fig = plt.figure('2D frame', figsize=figsize)
+ fig.canvas.mpl_connect('key_press_event', on_key_press)
+
+ print("按下空格键抓取图片,一次一张,保持稳定后才按,请不要长按连续拍摄,否则程序会识别出错退出,从而需要重新开始!")
+ print("抓取不同角度图片50张左右,之后按y键程序将进入下一阶段,自动完成等待正常退出即可,图片过少程序也会识别出错退出,从而需要重新开始!")
+
+ while KEEP_STREAM[0]:
+ p = get_frame_from_http()
+
+ show_frame(fig, p, save_once)
+ if save_once[0]:
+ save_once[0] = False
+ IMAGECOUNT += 1
+ tmp = "saved {} pictures".format(IMAGECOUNT)
+ logging.info(tmp)
+ print(tmp)
+ # 停顿时间
+ plt.pause(0.001)
+ # 清除当前画布
+ fig.clf()
+
+ plt.ioff()
+
+ rgblist = [a for a in os.listdir(
+ rgb_dir) if a[-3:] == 'png' or a[-3:] == 'jpg']
+ toflist = [a for a in os.listdir(
+ tof_dir) if a[-3:] == 'png' or a[-3:] == 'jpg']
+ if(len(rgblist) != len(toflist)):
+ print("error pic count not match")
+ else:
+ print("there are ", len(rgblist), ' pairs')
+ # for r,t in zip(rgblist,toflist):
+ # print(r,t)
+ rgbimg = []
+ tofimg = []
+ for r, t in zip(rgblist, toflist):
+ rimg = cv2.imread(rgb_dir+r)
+ rimg = cv2.cvtColor(rimg, cv2.COLOR_BGR2RGB)
+ timg = cv2.imread(tof_dir+t) # , cv2.IMREAD_GRAYSCALE)
+ rimg = cv2.resize(rimg, (IMG_WIDTH, IMG_HEIGHT))
+ timg = cv2.resize(timg, (IMG_WIDTH, IMG_HEIGHT))
+ rgbimg.append(rimg)
+ tofimg.append(timg)
+
+ criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001)
+ # pt_found_t,ptst = cv2.findChessboardCorners(tofimg[0], patternSize);
+ # print(pt_found_t,ptst)
+ # PLT.figure()
+ # PLT.imshow(tofimg[0])
+ # PLT.show()
+ reject_nums = 0
+ imgp_rgb = []
+ imgp_tof = []
+ for r, t in zip(rgbimg, tofimg):
+ pt_found_r, ptsr = cv2.findChessboardCornersSB(
+ r, patternSize, flags=cv2.CALIB_CB_NORMALIZE_IMAGE)
+ pt_found_t, ptst = cv2.findChessboardCornersSB(
+ t, patternSize, flags=cv2.CALIB_CB_NORMALIZE_IMAGE)
+ if(not (pt_found_r and pt_found_t)):
+ reject_nums += 1
+ # print(pt_found_r,pt_found_t)
+ continue
+ ptsr = cv2.cornerSubPix(cv2.cvtColor(
+ r, cv2.COLOR_RGB2GRAY), ptsr, (3, 3), (-1, -1), criteria)
+ ptst = cv2.cornerSubPix(cv2.cvtColor(
+ t, cv2.COLOR_RGB2GRAY), ptst, (3, 3), (-1, -1), criteria)
+ imgp_rgb.append(ptsr)
+ imgp_tof.append(ptst)
+ perview1 = cv2.drawChessboardCorners(r, patternSize, ptsr, pt_found_r)
+ perview2 = cv2.drawChessboardCorners(t, patternSize, ptst, pt_found_t)
+ # PLT.figure()
+ # PLT.imshow(perview1)
+ # PLT.show()
+ # PLT.figure()
+ # PLT.imshow(perview2)
+ # PLT.show()
+ tmp = "reject: {}, total: {}".format(reject_nums, len(rgbimg))
+ logging.warning(tmp)
+ print(tmp)
+
+ if reject_nums == len(rgbimg):
+ print("失败,请重新运行该程序,请保持标定板所有方格一直在画面中!")
+ return None
+
+ xi = np.linspace(0, patternSize[0]-1, patternSize[0])
+ yi = np.linspace(0, patternSize[1]-1, patternSize[1])
+ zpos = np.zeros((patternSize[1], patternSize[0]))
+ boardpos = np.array(np.meshgrid(xi, yi))*squareSize
+ boardpos = np.concatenate(([boardpos[0]], [boardpos[1]], [zpos]))
+ boardpos = np.transpose(boardpos, (1, 2, 0))
+ # print(boardpos)
+ cameraMatrix1 = np.zeros((3, 4))
+ cameraMatrix2 = np.zeros((3, 4))
+ distCoeffs1 = np.zeros((1, 5))
+ distCoeffs2 = np.zeros((1, 5))
+ R = np.zeros((3, 3)),
+ T = np.zeros((3, 1)),
+ # cbs=np.array([boardpos]*len(imgp_rgb))
+ # cbs=cbs.reshape((cbs.shape[0],cbs.shape[1]*cbs.shape[2],cbs.shape[3]))
+ cbs = np.array([boardpos.astype(np.float32)]*len(imgp_rgb))
+ cbs = cbs.reshape((cbs.shape[0], cbs.shape[1]*cbs.shape[2], cbs.shape[3]))
+ # print(cbs)
+ ret, cameraMatrix1, distCoeffs1, rvecs, tvecs = cv2.calibrateCamera(
+ cbs, imgp_rgb, (IMG_WIDTH, IMG_HEIGHT), cameraMatrix1, distCoeffs1)
+ ret, cameraMatrix2, distCoeffs2, rvecs, tvecs = cv2.calibrateCamera(
+ cbs, imgp_tof, (IMG_WIDTH, IMG_HEIGHT), cameraMatrix2, distCoeffs2)
+ flags = cv2.CALIB_FIX_INTRINSIC+cv2.CALIB_USE_INTRINSIC_GUESS + \
+ cv2.CALIB_FIX_PRINCIPAL_POINT+cv2.CALIB_FIX_FOCAL_LENGTH+cv2.CALIB_FIX_ASPECT_RATIO
+ ret, K1, D1, K2, D2, R, T, E, F = cv2.stereoCalibrate(
+ cbs, imgp_rgb, imgp_tof, cameraMatrix1, distCoeffs1, cameraMatrix2, distCoeffs2, (IMG_WIDTH, IMG_HEIGHT), flags=flags)
+ print("ret", ret)
+ print("K1", K1)
+ print("D1", D1)
+ print("K2", K2)
+ print("D2", D2)
+ print("R", R)
+ print("T", T)
+ print("E", E)
+ print("F", F)
+ param = {
+ "R_Matrix_data": R.reshape(-1).tolist(),
+ "T_Vec_data": T.reshape(-1).tolist(),
+ "Camera_Matrix_data": cameraMatrix1.reshape(-1).tolist(),
+ "Distortion_Parm_data": distCoeffs1.reshape(-1).tolist(),
+ }
+
+ j = json.dumps(param, indent=4)
+ logging.info(j)
+ print(j)
+ with open('CameraParms.json', 'w') as fp:
+ fp.write(j)
+ fp.close()
+
+
+if __name__ == '__main__':
+ main()
+```
diff --git a/docs/hardware/zh/metasense/metasense-a010/metasense-a010.md b/docs/hardware/zh/metasense/metasense-a010/metasense-a010.md
index 2fdbffb6..df518603 100644
--- a/docs/hardware/zh/metasense/metasense-a010/metasense-a010.md
+++ b/docs/hardware/zh/metasense/metasense-a010/metasense-a010.md
@@ -52,11 +52,11 @@ MetaSense-A010 是由 Sipeed 所推出的一款由 BL702 + 炬佑 100x100 TOF
**COMTOOL 软件包**
Windows 系统连接:下载压缩包后解压安装即可。
-Linux 系统:不提供软件压缩包,需用户自行编译。
+Linux 系统:不提供软件压缩包,需用户自行编译 [获取跳转](https://github.com/sipeed/COMTool)。
注意:Win 7 及以下系统需装驱动,可自行前往 FTDI 官网下载。
### 上电互动预览
-将设备通电后,可在设备上自带 LCD 屏实时预览 color map 后的深度伪彩图
+将设备通电后,可在设备上自带 LCD 屏实时预览 color map 后的深度伪彩图。

### PC 预览和微调
@@ -84,45 +84,33 @@ COMTOOL 上位机的配置控件说明
- FPS 设置出图帧率(不宜过高,根据对接设备的性能合理设置即可,减小帧率可以减少传输数据量)
- Ev 曝光间隙控制(最左代表 AE,其他是固定曝光时间)
+## 案例:远近中物体实拍
+物体摆放距离形成差异,模组通过捕捉到的深度值的差异显示冷暖色调,更直观了解 `TOF` 技术。
+
## 案例:检测人流
+高精度,大分辨率的实时监测人流走动的情况并统计。
-高精度,大分辨率的实时监测人流走动的情况统计。
-
-
-
-
+
## 案例:键盘灯跟随
-
实现超酷炫的键盘灯跟随,实时跟踪手部的位置,再根据手部的位置映射键盘灯。
-
+
## 案例:接入 MCU
-
MS-A010 拥有强大的兼容性,可基于串口协议外接 K210 bit 这样的单片机开发板或树莓派之类的 linux 开发板来进行二次开发。
+[MS-A010 外接 K210 bit 源码获取](./metasense-a010/../code.html#tof_mainpy)
+
-
-获取链接:
## 二次开发:串口协议
可参考上方的案例:**K210 bit 外接 MCU**
-| AT | |
-| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| +ISP
Image Signal Processor | :0: turn ISP off
=1: turn ISP on |
-| +BINN
full binning | =1: output 100x100 pixel frame
=2: output 50x50 pixel frame
=4: output 25x25 pixel frame
|
-| +DISP
display mux | =0: all off
=1: lcd display on
=2: usb display on
=3: lcd and usb display on
=4: uart display on
=5: lcd and uart display on
=6: usb and uart display on
=7: lcd, usb and uart display on |
-| +BAUD
uart baudrate | =0: 9600
=1: 57600
=2: 115200
=3: 230400
=4: 460800
=5: 921600
=6: 1000000
=7: 2000000
=8: 3000000 |
-| +UNIT
quantization unit | =0: auto
=1-10: quantizated by unit(mm) |
-| +FPS
frame per second | =1-19: set frame per second |
-| +Save
save config | : save current configuration |
-
@@ -130,7 +118,7 @@ MS-A010 拥有强大的兼容性,可基于串口协议外接 K210 bit 这样
### 接入 ROS1
**1. 准备工作**
-首先,准备适用的环境:Linux 系统
+首先,准备适用的环境:`Linux` 系统
可使用虚拟机 `virtual box` 或者 `vmware` 也可安装双系统,安装方法请自行查询。
**2. 安装运行**
@@ -151,13 +139,13 @@ rosrun sipeed_tof_ms_a010 a010_publisher _device:="/dev/ttyUSB0"
**4. RVIZ2 预览**
打开 `rviz2` 后,在界面左下角的 `Add`->`By topic`->`PointCloud2或/depth` ->`Image 添加` ->`Display/Global Options/Fixed Frame` 需要修改成 `tof`,才能正常显示点云,根据添加的内容,左侧会显示 `Image` 而中间则显示点云。
-
+
### 接入 ROS2
**1. 准备工作**
-首先,准备适用的环境:Linux 系统
+首先,准备适用的环境:`Linux` 系统
可使用虚拟机 `virtual box` 或者 `vmware` 也可安装双系统,安装方法请自行查询。
**2. 安装运行**
@@ -175,9 +163,9 @@ source install/setup.sh
**3. RQT 查看帧率**
-
+
**4. RVIZ2 预览**
打开 `rviz2` 后,在界面左下角的 `Add`->`By topic`->`PointCloud2或/depth` ->`Image 添加` ->`Display/Global Options/Fixed Frame` 需要修改成 `tof`,才能正常显示点云,根据添加的内容,左侧会显示 `Image` 而中间则显示点云。
-
+
diff --git a/docs/hardware/zh/metasense/metasense-a075v/assets/tof-15.jpg b/docs/hardware/zh/metasense/metasense-a075v/assets/tof-15.jpg
new file mode 100644
index 00000000..a21567f5
Binary files /dev/null and b/docs/hardware/zh/metasense/metasense-a075v/assets/tof-15.jpg differ
diff --git a/docs/hardware/zh/metasense/metasense-a075v/assets/tof-9.png b/docs/hardware/zh/metasense/metasense-a075v/assets/tof-9.jpg
similarity index 100%
rename from docs/hardware/zh/metasense/metasense-a075v/assets/tof-9.png
rename to docs/hardware/zh/metasense/metasense-a075v/assets/tof-9.jpg
diff --git a/docs/hardware/zh/metasense/metasense-a075v/assets/tog-14.jpg b/docs/hardware/zh/metasense/metasense-a075v/assets/tog-14.jpg
new file mode 100644
index 00000000..27737d73
Binary files /dev/null and b/docs/hardware/zh/metasense/metasense-a075v/assets/tog-14.jpg differ
diff --git a/docs/hardware/zh/metasense/metasense-a075v/metasense-a075v.md b/docs/hardware/zh/metasense/metasense-a075v/metasense-a075v.md
index 7d4ff22f..1095b41c 100644
--- a/docs/hardware/zh/metasense/metasense-a075v/metasense-a075v.md
+++ b/docs/hardware/zh/metasense/metasense-a075v/metasense-a075v.md
@@ -18,7 +18,9 @@ MS-A075V 是由 Sipeed 所推出的一款具有 RGB 功能的 3D TOF 摄像机
## 产品开箱指南
### 准备工作
-**接线说明**:
+**接线说明**
+
+
**安装驱动**:上电前一定要确保网络环境中没有使用 192.168.233.0/24 的地址段,MS-A075V 会使用 RNDIS 并设置自己的 ip 地址为 http://192.168.233.1
Windows 系统需安装驱动才可正常运行。
@@ -28,7 +30,7 @@ Windows 系统需安装驱动才可正常运行。
### 网页上位机预览
1. 把设备使用 type-c 线与电脑链接,MS-A075V 的风扇会开始工作,产品正面镜头处就会闪烁红灯。
-2. 此时可打开浏览器输入 http://192.168.233.233 预览 3D 点云图,上电后有延迟需等待一段时间后,系统和程序才会启动完成。
+2. 此时可打开浏览器输入 http://192.168.233.1 预览 3D 点云图,上电后有延迟需等待一段时间后,系统和程序才会启动完成。
3. 使用网页上位机快速预览 演示图(正面和侧面):
@@ -67,6 +69,10 @@ Windows 系统需安装驱动才可正常运行。
- **SavePointCloud**:可保存一帧 3D 点云图,保存格式为 pcd ,同样可以通过上述提供的脚本预览。
注意:raw 数据可通过开放的接口获取,开发者进行解析即可基于此二次开发,但点云(pointcloud)是基于 raw 数据和相机内参进行计算得到的,无相应接口提供。
+## 案例:远中近点云实拍
+高精度的映射物品摆放距离的差异,点云图可直观清楚感受到更真实的可视化。
+
+
## 案例:避障小车
模组可搭载小车或无人机来回移动获取障碍物的远近深度值,并通过差异判断画面中是否有障碍物,做出快速反应并精准规避障碍物(例程暂未开源,待整理公开)。
@@ -93,10 +99,11 @@ Windows 系统需安装驱动才可正常运行。
### 解包推流
理解了上述 `python SDK` 数据获取和解码的逻辑后,我们可以尝试进阶版,连续获取解码并调用第三方 `python` 图像库,例如:matplotlib 进行实时显示。而 `toturial.py` 给出了获取一帧数据的逻辑实现,通过 plt 显示并外套循环即可做到实时显示。
-**解包推流**:
+**解包推流**:`python stream.py` [点我查看stream.py内容](./../metasense-a010/code.html#streampy)
**使用方式**:装好所有的依赖包后即可 `python stream.py` 运行。

+[233](./../metasense-a010/code.html#calibratepy)
### 检测体积
基于第三方 `python` 包,理解了上述数据获取和解码的逻辑后,再次进阶,不但持续显示多帧并且再通过 SDK 获取相机内参后计算出初略的点云,做累加得到总体积。限制:要求俯视图可以看到除底面外的所有细节
@@ -111,7 +118,7 @@ Windows 系统需安装驱动才可正常运行。
### 接入 ROS1
**1. 准备工作**
-首先,准备适用的环境:Linux 系统
+首先,准备适用的环境:`Linux` 系统
可使用虚拟机 `virtual box` 或者 `vmware` 也可安装双系统,安装方法请自行查询。
**2. 安装运行**
@@ -136,7 +143,7 @@ rosrun sipeed_tof_cpp publisher
### 接入 ROS2
**1. 准备工作**
-首先,准备适用的环境:Linux 系统
+首先,准备适用的环境:`Linux` 系统
可使用虚拟机 `virtual box` 或者 `vmware` 也可安装双系统,安装方法请自行查询。
**2. 安装运行**
diff --git a/docs/hardware/zh/metasense/readme.md b/docs/hardware/zh/metasense/readme.md
index 0cd293a0..e8f03b40 100644
--- a/docs/hardware/zh/metasense/readme.md
+++ b/docs/hardware/zh/metasense/readme.md
@@ -4,9 +4,10 @@ title: MetaSense 系列
## MetaSense 是什么?
-MetaSense 系列摄像头模组分为 MetaSense-A075V 和 MetaSense-A010 两款产品。
-- MS-A010 是由 Sipeed 所推出的一款由 BL702 + 炬佑 100x100 TOF 模组所组成的极致性价比的 TOF 3D 传感器模组,最大支持 100x100 的分辨率和 8 位精度,并且带有 240×135 的 LCD 显示屏可实时预览 color map 后的深度图。
-- 而 MS-A075V 是一款具有 RGB 功能的 3D TOF 摄像机模组,该模组可以实现免驱的即插即用,实现实时彩色 3D 显示。
+MetaSense 系列产品搭载 TOF 深度摄像头,目前主要有 MetaSense-A010 和 MetaSense-A075VS 两款产品。
+- MS-A010 是一款由 BL702 + 炬佑 100x100 TOF 模组所组成的极致性价比的 TOF 3D 传感器模组,最大支持 100x100 的分辨率和 8 位精度,并且带有 240×135 的 LCD 显示屏可实时预览 color map 后的深度图。
+- 而 MS-A075V 是一款具有 RGB 功能的 3D TOF 摄像机模组,该模组可以实现 Linux 免驱的即插即用,实现实时彩色 3D 显示。
+
@@ -15,7 +16,7 @@ MetaSense 系列摄像头模组分为 MetaSense-A075V 和 MetaSense-A010 两款
-| |MateSense-010
| MateSense-A075V
|
+| |MateSense-A010
| MateSense-A075V
|
| :----------- |:----------------------------------------------- | :------------------------------------------------- |
| | |  |
| 接口 | 1.25mm 串口连接器 \*1
Type-C USB2.0 \*1 | 1.25mm 串口连接器 \*1
Type-C USB2.0 \*1 |
@@ -26,7 +27,6 @@ MetaSense 系列摄像头模组分为 MetaSense-A075V 和 MetaSense-A010 两款
| 测量范围 |0.2-2.5m | 0.15-1.5m |
| 测量精度 |<=1%/cm | <=1%/cm |
-相关交流社区及社群:
@@ -34,56 +34,49 @@ MetaSense 系列摄像头模组分为 MetaSense-A075V 和 MetaSense-A010 两款
### 案例:远中近物体实拍
-
+高精度的映射物品摆放距离的差异,点云图可直观感受到更真实的可视化,

### 案例:人流统计
+可高精度、大分辨率的实时监测人流走动的情况并统计。
-高精度,大分辨率的实时监测人流走动的情况统计。
-
-
+
### 案例:小车避障
-
可搭载于小车移动并判断画面是否有障碍物,模组自带 LCD 屏幕精准显示距离并做出反应规避障碍物。

### 案例:键盘灯跟随
-
实现超酷炫的键盘灯跟随,实时跟踪手部的位置,再根据手部的位置映射键盘灯。
-
+
### 案例:体积测量
-
通过 SDK 获取到的相机内参后计算出初略的点云,做累加得到全部的总体积,达到体积测量的效果。
-

-### 案例:外接 MCU
+### [案例:外接 MCU](./metasense-a010/code.html#tof_mainpy)
-MS-A010 拥有强大的兼容性,基于串口协议,可外接 K210 bit 这样的单片机开发板或树莓派之类的 linux 开发板来进行二次开发
-
-
+MS-A010 拥有强大的兼容性,基于串口协议的数据传输,可外接 K210 bit 这样的单片机开发板或树莓派之类的 linux 开发板来进行二次开发。
+
### 案例:接入 ROS1 + ROS2
-
双支持 ROS 系统,开放 ROS1+ROS2 接入功能包,可快速获得深度数据及深度图。
-
-
+
+
## 快速了解 TOF 技术
1. TOF: Time of flight(飞行时间)它是一种测距的方法,通过测量超声波/微波/光等信号在发射器和反射器之间的“飞行时间”来计算出两者之间的距离。能够实现 TOF 测距的传感器就是 TOF 传感器。种类较多,使用较多的是通过红外或者激光进行测距的 TOF 传感器。
-2. RGBD:RGB 摄像头加 TOF 深度摄像头组成的一整个模组。图源自[百度](https://baike.baidu.com/item/TOF/19952376?fr=aladdin)。
-
+2. 物体摆放距离形成差异,模组通过捕捉到的深度值的差异显示冷暖色调,冷暖色随着对距离的映射发生变化,距离越近色调呈暖调(橘红)而越远色调呈冷调(蓝色)
+
## 更多
-关于 MS-010 更详细的资料获取:
-关于 MS-075 更详细的资料获取:
\ No newline at end of file
+关于 MS-010 更详细的资料获取:[点击跳转](https://wiki.sipeed.com/hardware/zh/metasense/metasense-a010/metasense-a010.html)
+关于 MS-075 更详细的资料获取:[点击跳转](https://wiki.sipeed.com/hardware/zh/metasense/metasense-a075v/metasense-a075v.html)
\ No newline at end of file
diff --git a/pages/index/en/metasense.md b/pages/index/en/metasense.md
new file mode 100644
index 00000000..070061ae
--- /dev/null
+++ b/pages/index/en/metasense.md
@@ -0,0 +1,4 @@
+---
+layout: redirect
+redirect_url: https://wiki.sipeed.com/hardware/en/metasense/index.html
+---
\ No newline at end of file
diff --git a/pages/index/en/metasense10.md b/pages/index/en/metasense10.md
new file mode 100644
index 00000000..788d10d8
--- /dev/null
+++ b/pages/index/en/metasense10.md
@@ -0,0 +1,4 @@
+---
+layout: redirect
+redirect_url: https://wiki.sipeed.com/hardware/en/metasense/metasense-a010/metasense-a010.html
+---
\ No newline at end of file
diff --git a/pages/index/en/metasense75.md b/pages/index/en/metasense75.md
new file mode 100644
index 00000000..29734764
--- /dev/null
+++ b/pages/index/en/metasense75.md
@@ -0,0 +1,4 @@
+---
+layout: redirect
+redirect_url: https://wiki.sipeed.com/hardware/en/metasense/metasense-a075v/metasense-a075v.html
+---
\ No newline at end of file