563浏览
查看: 563|回复: 0

[项目分享] AI画像打印机:基于行空板M10与二哈2的实时人像打印装置

[复制链接]
本帖最后由 云天 于 2026-3-17 09:41 编辑

【项目简介】
       你是否想过拥有一台能“看懂”你、并为你绘制素描人像的机器?本项目利用“行空板M10”作为主控,连接“二哈识图2 AI视觉传感器”进行人脸检测与定位,通过语音引导用户调整至最佳拍摄位置。然后,利用二哈2的实时图传功能获取人脸图像,上传至“SiliconFlow API”生成素描风格图片,最后通过“嵌入式热敏打印机”将素描打印出来。整个过程充满互动性与科技感,适合作为AIoT跨学科教学案例。

【硬件清单】

组件
型号/链接
作用
主控板行空板M10 (UNIHIKER)运行核心Python程序,处理图像与逻辑
AI视觉传感器二哈识图2 (HuskyLens 2)实时人脸识别、坐标定位、图像采集
热敏打印机嵌入式热敏打印机V2.0 (DFR0503)打印最终素描作品
USB转TTL模块6合1多功能串口转换器行空板与打印机之间的串口通信桥梁
其他12V/2.5A电源(给打印机)、Type-C线(给行空板)、PH2.0连接线、热敏纸卷供电与连接

【硬件接线】

       1.二哈2 → 行空板M10 (I2C接口)

二哈2 (4Pin Gravity接口)
行空板M10 (I2C接口)
红线 (VCC)3.3V (或5V,取决于二哈供电需求)
黑线 (GND)GND
蓝线 (SDA)SDA
绿线 (SCL)SCL

       注:行空板M10的I2C接口位于板载4Pin接口上。

       2.热敏打印机 → USB转TTL模块 → 行空板M10
       由于打印机使用TTL电平串口,而行空板USB-A口可直接识别USB转TTL模块,接线如下:

打印机 (TTL接口)
USB转TTL模块
TXD (黑线)RXD
RXD (蓝线)TXD
GND (绿线)GND
DTR (红线)悬空

       然后将USB转TTL模块插入行空板的**USB-A口**。在行空板系统中,该模块通常被识别为`/dev/ttyUSB0`。

       3.打印机独立供电
       务必使用9-24V/2.5A以上的电源为打印机单独供电,打印图像时电流可达2.5A,仅靠USB供电无法驱动。
【结构设计】
AI画像打印机:基于行空板M10与二哈2的实时人像打印装置图4


AI画像打印机:基于行空板M10与二哈2的实时人像打印装置图3


AI画像打印机:基于行空板M10与二哈2的实时人像打印装置图2


AI画像打印机:基于行空板M10与二哈2的实时人像打印装置图5


AI画像打印机:基于行空板M10与二哈2的实时人像打印装置图6


AI画像打印机:基于行空板M10与二哈2的实时人像打印装置图8


AI画像打印机:基于行空板M10与二哈2的实时人像打印装置图1

【核心原理与代码解析】

       1.人脸检测与引导逻辑
       二哈2通过I2C实时返回人脸框的坐标和宽度。程序根据人脸在画面中的位置(`xCenter`, `yCenter`)和大小(`width`),通过语音模块播放相应的提示音,引导用户调整,确保人脸处于最佳拍摄区域。

  1. # 核心判断逻辑示例
  2. if xCenter > 750:
  3.        huskylens.playMusic("right.mp3", 100)  # 请向右一点
  4. elif xCenter < 300:
  5.        huskylens.playMusic("left.mp3", 100)   # 请向左一点
  6. # ... 其他方向与距离判断
复制代码

       2. 图像采集与AI素描生成
       当人脸位置合适时,程序利用OpenCV通过二哈2的RTSP图传地址(如`rtsp://192.168.31.180:8554/live`)抓取一帧图像。然后调用“SiliconFlow API”,将图像上传并请求生成素描风格图片。这里选择支持图像输入的模型(如`Qwen/Qwen-Image-Edit-2509`),并通过提示词引导生成“铅笔素描画”。

       3.热敏打印技术要点
       打印机使用ESC/POS指令集。打印图像需使用 `GS v 0` 命令,将处理好的黑白位图数据发送给打印机。
       图像处理:将生成的彩色素描图缩放到打印机宽度384像素,转为灰度图,再二值化为纯黑白(1位位图)。注意:黑色像素(值为0)对应打印点。
       数据编码:每行384点需编码为48字节,每字节的8个位对应一行中的8个连续像素。
       模式切换:务必在打印前发送切换小票模式的指令(`0x1F 0x2F 0x0B 0x00 0x01 0x00 0x00`)和初始化命令(`ESC @`)。

  1. # 图像数据编码核心片段
  2. for y in range(img_height):
  3.     for x_byte in range(48):
  4.         byte_val = 0
  5.         for bit in range(8):
  6.             x = x_byte * 8 + bit
  7.             if binary[y, x] == 0:  # 黑色像素
  8.                 byte_val |= (1 << (7 - bit))
  9.         image_data.append(byte_val)
复制代码

【完整代码】
        
       获取硅基流动API:用于根据生成图片。通过第三方的硅基流动注册获取API,如方便注册,使用我的邀请码注册:https://cloud.siliconflow.cn/i/KwyEBX3e,邀请码:KwyEBX3e。共同获取免费额度。如果不方便注册,可使用我的API:sk-kxwsrzianqfxsebnihblrgyyytrrtgvvdjvdiujcuvwymrfp。
        主要结构如下:
       1. 初始化:导入库、初始化行空板、二哈2和打印机串口。
       2.  辅助函数:`generate_sketch_from_face()` 调用API生成素描;`print_image_to_printer()` 处理图像并发送打印指令。
       3.  主循环:持续获取人脸数据,判断位置并语音引导;条件满足时抓图、生成素描、显示并打印。
  1. # -*- coding: UTF-8 -*-
  2. import sys
  3. sys.path.append("/root/mindplus/.lib/thirdExtension/dfrobot-huskylensv2-thirdex")
  4. import cv2
  5. import time
  6. import base64
  7. import requests
  8. import numpy as np
  9. import serial
  10. from pinpong.board import Board
  11. from pinpong.extension.unihiker import *
  12. from dfrobot_huskylensv2 import *
  13. from unihiker import GUI
  14. runtime = time.time()
  15. # ==================== 配置区域 ====================
  16. # SiliconFlow API 配置
  17. API_KEY = "sk-******************************************************"                     # 替换为你的有效密钥
  18. API_URL = "https://api.siliconflow.cn/v1/images/generations"
  19. MODEL_NAME = "Qwen/Qwen-Image-Edit-2509"      # 需支持图像输入的模型
  20. HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
  21. # 热敏打印机串口配置(通过USB转TTL模块连接)
  22. PRINTER_PORT = "/dev/ttyUSB0"                 # Unihiker上通常为 /dev/ttyUSB0
  23. PRINTER_BAUDRATE = 115200
  24. # =================================================
  25. u_gui=GUI()
  26. background=u_gui.draw_image(image="logo.png",x=0,y=0)
  27. # 初始化PinPong板子和HuskyLens
  28. Board().begin()
  29. print("系统初始化...")
  30. huskylens = HuskylensV2_I2C()
  31. huskylens.knock()
  32. huskylens.switchAlgorithm(ALGORITHM_FACE_RECOGNITION)
  33. time.sleep(5)
  34. # 初始化打印机串口
  35. try:
  36.     printer_serial = serial.Serial(PRINTER_PORT, PRINTER_BAUDRATE, timeout=1)
  37.     time.sleep(2)  # 等待打印机就绪
  38.     print("打印机串口已打开")
  39. except Exception as e:
  40.     print(f"无法打开打印机串口: {e}")
  41.     printer_serial = None
  42. # ==================== 辅助函数 ====================
  43. def generate_sketch_from_face(image_cv2):
  44.     """
  45.     将OpenCV图像(BGR格式)发送给SiliconFlow API生成素描画。
  46.     返回: 成功返回素描图(cv2格式),失败返回None。
  47.     """
  48.     if image_cv2 is None:
  49.         return None
  50.     # 1. 图像编码为JPEG + Base64
  51.     ret, buffer = cv2.imencode('.jpg', image_cv2)
  52.     if not ret:
  53.         print("图像编码失败")
  54.         return None
  55.     base64_image = base64.b64encode(buffer).decode('utf-8')
  56.     data_uri = f"data:image/jpeg;base64,{base64_image}"
  57.     # 2. 构造请求载荷
  58.     payload = {
  59.         "model": MODEL_NAME,
  60.         "prompt": "将这张人脸照片变成一幅精美的铅笔素描画,黑白线条,高对比度",
  61.         "image": data_uri,
  62.         "image_size": "1472x1140"
  63.     }
  64.     # 3. 发送请求
  65.     try:
  66.         print("正在请求SiliconFlow API生成素描...")
  67.         response = requests.post(API_URL, headers=HEADERS, json=payload, timeout=30)
  68.         response.raise_for_status()
  69.         result = response.json()
  70.         # 4. 解析返回的图片URL
  71.         if result.get('images') and len(result['images']) > 0:
  72.             image_url = result['images'][0]['url']
  73.             print(f"素描图生成成功,临时URL: {image_url}")
  74.             # 5. 下载图片
  75.             img_resp = requests.get(image_url, timeout=15)
  76.             img_resp.raise_for_status()
  77.             img_array = np.frombuffer(img_resp.content, dtype=np.uint8)
  78.             sketch_img = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
  79.             return sketch_img
  80.         else:
  81.             print(f"API返回异常: {result}")
  82.             return None
  83.     except Exception as e:
  84.         print(f"API请求失败: {e}")
  85.         return None
  86. def print_image_to_printer(image_cv2, printer_serial):
  87.     if image_cv2 is None or printer_serial is None:
  88.         print("图像或打印机串口无效")
  89.         return
  90.    
  91.     # 切换小票模式(可选)
  92.     # printer_serial.write(bytes([0x1F, 0x2F, 0x0B, 0x00, 0x01, 0x00, 0x00]))
  93.     time.sleep(0.2)
  94.    
  95.     # 初始化打印机
  96.     printer_serial.write(b'\x1B\x40')  # ESC @
  97.     time.sleep(0.1)
  98.     target_width = 384  # 有效打印宽度384点(48mm)
  99.     h, w = image_cv2.shape[:2]
  100.     target_height = int(h * (target_width / w))
  101.     print(f"原始 {w}x{h} -> 目标 {target_width}x{target_height}")
  102.     resized = cv2.resize(image_cv2, (target_width, target_height))
  103.     gray = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY)
  104.     _, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
  105.     cv2.imwrite("debug_binary.png", binary)
  106.     img_height = binary.shape[0]
  107.     x_bytes = (target_width + 7) // 8  # 每行字节数
  108.     image_data = bytearray()
  109.     for y in range(img_height):
  110.         for x_byte in range(x_bytes):
  111.             byte_val = 0
  112.             for bit in range(8):
  113.                 x = x_byte * 8 + bit
  114.                 if x < target_width and binary[y, x] == 0:  # 黑色像素
  115.                     byte_val |= (1 << (7 - bit))
  116.             image_data.append(byte_val)
  117.     # 构建GS v 0命令头
  118.     mode = 0x00  # 普通模式
  119.     xL = x_bytes & 0xFF
  120.     xH = (x_bytes >> 8) & 0xFF
  121.     yL = img_height & 0xFF
  122.     yH = (img_height >> 8) & 0xFF
  123.     print(f"发送高度: {img_height} 点, 每行 {x_bytes} 字节")
  124.     print(f"命令参数: xL={xL}, xH={xH}, yL={yL}, yH={yH}")
  125.     # 发送指令
  126.     try:
  127.         printer_serial.write(bytes([0x1D, 0x76, 0x30, mode, xL, xH, yL, yH]))
  128.         printer_serial.write(image_data)
  129.         printer_serial.flush()
  130.         print("图像数据发送完成")
  131.     except Exception as e:
  132.         print(f"发送失败: {e}")
  133. # ==================== 主循环 ====================
  134. huskylens.playMusic("pic.mp3", 100)
  135. print("进入主循环,等待人脸...")
  136. while True:
  137.     time.sleep(0.5)
  138.     huskylens.getResult(ALGORITHM_FACE_RECOGNITION)
  139.     if huskylens.available(ALGORITHM_FACE_RECOGNITION):
  140.      print("检测到人脸",end="")
  141.      print(huskylens.getCachedCenterResult(ALGORITHM_FACE_RECOGNITION).width)
  142.      if (time.time() - runtime) > 5:   # 每5秒处理一次
  143.       if(huskylens.getCachedCenterResult(ALGORITHM_FACE_RECOGNITION).xCenter>750):
  144.         huskylens.playMusic("right.mp3", 100)#播报语音:请向右一点
  145.         time.sleep(3)
  146.       elif(huskylens.getCachedCenterResult(ALGORITHM_FACE_RECOGNITION).xCenter<300):
  147.         huskylens.playMusic("left.mp3", 100)#播报语音:请向左一点
  148.         time.sleep(3)
  149.       elif(huskylens.getCachedCenterResult(ALGORITHM_FACE_RECOGNITION).yCenter<250):
  150.         huskylens.playMusic("down.mp3", 100)#播报语音:请向下一点
  151.         time.sleep(3)
  152.       elif(huskylens.getCachedCenterResult(ALGORITHM_FACE_RECOGNITION).yCenter>450):
  153.         huskylens.playMusic("up.mp3", 100)#播报语音:请向上一点
  154.         time.sleep(3)
  155.       elif(huskylens.getCachedCenterResult(ALGORITHM_FACE_RECOGNITION).width>250):
  156.         huskylens.playMusic("far.mp3", 100)#播报语音:请离远一点
  157.         time.sleep(3)
  158.       elif(huskylens.getCachedCenterResult(ALGORITHM_FACE_RECOGNITION).width<150):
  159.         huskylens.playMusic("near.mp3", 100)#播报语音:请离近一点
  160.         time.sleep(3)
  161.       else:
  162.             runtime = time.time()
  163.             buzzer.pitch(131, 4)# 蜂鸣提示
  164.             huskylens.playMusic("wait.mp3", 100)#播报语音:请保持,正在拍照中
  165.             # 1. 从RTSP流抓取一帧图像
  166.             vd = cv2.VideoCapture("rtsp://192.168.31.180:8554/live")
  167.             if not vd.isOpened():
  168.                 print("无法打开RTSP流")
  169.                 continue
  170.             ret, frame = vd.read()
  171.             vd.release()
  172.             if not ret or frame is None:
  173.                 print("获取图像失败")
  174.                 continue
  175.             huskylens.playMusic("running.mp3", 100)#播报语音:拍照完成,正在生成画像中
  176.             # 2. 生成素描图
  177.             sketch = generate_sketch_from_face(frame)
  178.             # 3. 显示并打印
  179.             if sketch is not None:
  180.                 print("成功获取素描图,准备显示...")
  181.                 cv2.namedWindow("Sketch", cv2.WINDOW_NORMAL)
  182.                 cv2.setWindowProperty("Sketch", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
  183.                 cv2.imshow("Sketch", sketch)
  184.                 cv2.waitKey(1)   # 必须加waitKey才能显示
  185.                 huskylens.playMusic("print.mp3", 100)#播报语音:画像打印中
  186.                 # 打印素描图
  187.                 if printer_serial:
  188.                     print_image_to_printer(sketch, printer_serial)
  189.                 # 保持显示5秒
  190.                 time.sleep(5)
  191.                 cv2.destroyAllWindows()
  192.                 vd.release()
  193.             else:
  194.                 print("素描生成失败,显示原始图像")
  195.                 cv2.namedWindow("Original", cv2.WINDOW_NORMAL)
  196.                 cv2.setWindowProperty("Original", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
  197.                 cv2.imshow("Original", frame)
  198.                 cv2.waitKey(1)
  199.                 time.sleep(5)
  200.                 cv2.destroyAllWindows()
  201.                 vd.release()
  202.             runtime = time.time()
复制代码

【运行效果】
AI画像打印机:基于行空板M10与二哈2的实时人像打印装置图7

       1.  开机:行空板屏幕显示Logo,程序启动,二哈2开始检测人脸。
       2.  引导:当用户出现在镜头前,装置会根据人脸位置和大小播放语音(如“请向左一点”、“请离近一点”),引导用户至最佳拍摄点。
       3.  拍摄与生成:位置合适后,蜂鸣器提示,进入拍照状态。屏幕短暂显示“正在生成画像”,同时调用云端API绘制素描。
       4.  打印:素描生成后,屏幕全屏显示,同时打印机开始打印。约10-20秒后,一张专属的AI素描画像便从打印机中缓缓吐出。

【项目总结与扩展】

      本项目成功地将“边缘AI计算”(二哈2)、“云端AIGC”(SiliconFlow)和“物理输出”(热敏打印)结合在一起,创造了一个完整的互动艺术装置。它展示了如何利用行空板M10强大的Python生态,快速整合多种外设与在线服务。




您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2026 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail