鱼C论坛

 找回密码
 立即注册
查看: 1271|回复: 5

[已解决]关于用树莓派配合小幻熊视觉模块巡线走路机器人

[复制链接]
发表于 2023-4-26 20:51:45 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
请问我买了树莓派想要用python搭配小幻熊ai视觉模块进行巡线行走,现在有能识别线条id的程序,怎么让它能在识别到黑线id的时候进行走路[code]import time
import struct
from smbus2 import SMBus, i2c_msg

WONDERCAM_REG_SYS_FIRMWARE_VERSION = (0x0000)
WONDERCAM_REG_SYS_LIGHT_STATE = (0x0030)
WONDERCAM_REG_SYS_CURRENT_FUNC = (0x0035)

WONDERCAM_REG_FACE_DETECT_BASE = (0x0400)
WONDERCAM_REG_OBJ_DETECT_BASE = (0x0800)
WONDERCAM_REG_CLASSIFICATION_BASE = (0x0C00)
WONDERCAM_REG_FEATURE_LEARNING_BASE = (0x0E00)
WONDERCAM_REG_COLOR_DETECT_BASE = (0x1000)
WONDERCAM_REG_LINE_FOLLOWING_BASE = (0x1400)
WONDERCAM_REG_APRILTAG_BASE = (0x1E00)
WONDERCAM_REG_QRCODE_BASE = (0x1800)
WONDERCAM_REG_BARCODE_BASE = (0x1C00)

WONDERCAM_FUNC_NONE = (0x00)
WONDERCAM_FUNC_FACE_DETECT = (0x01)
WONDERCAM_FUNC_OBJ_DETECT = (0x02)
WONDERCAM_FUNC_CLASSIFICATION = (0x03)
WONDERCAM_FUNC_FEATURE_LEARNING = (0x04)
WONDERCAM_FUNC_COLOR_DETECT = (0x05)
WONDERCAM_FUNC_LINE_FOLLOWING = (0x06)
WONDERCAM_FUNC_APRILTAG = (0x07)
WONDERCAM_FUNC_QRCODE = (0x08)
WONDERCAM_FUNC_BARCODE = (0x09)
WONDERCAM_FUNC_NUMBER_REC = (0x0A)
WONDERCAM_FUNC_LANDMARK_REC = (0x0B)

WONDERCAM_OBJ_AIRPLANE = (1)
WONDERCAM_OBJ_BICYCLE = (2)
WONDERCAM_OBJ_BIRD = (3)
WONDERCAM_OBJ_BOAT = (4)
WONDERCAM_OBJ_BOTTLE = (5)
WONDERCAM_OBJ_BUS = (6)
WONDERCAM_OBJ_CAR = (7)
WONDERCAM_OBJ_CAT = (8)
WONDERCAM_OBJ_CHAIR = (9)
WONDERCAM_OBJ_COW = (10)
WONDERCAM_OBJ_DINING_TABLE = (11)
WONDERCAM_OBJ_DOG = (12)
WONDERCAM_OBJ_HORSE = (13)
WONDERCAM_OBJ_MOTORBIKE = (14)
WONDERCAM_OBJ_PERSON = (15)
WONDERCAM_OBJ_POTTED_PLANT = (16)
WONDERCAM_OBJ_SHEEP = (17)
WONDERCAM_OBJ_SOFA = (18)
WONDERCAM_OBJ_TRAIN = (19)
WONDERCAM_OBJ_MONITOR = (20)

class WonderCam:

    def __init__(self, i2c_addr=0x32, i2c_adapter=1):
        self.i2c_adapter = i2c_adapter
        self.i2c_addr = i2c_addr
        self.summary = b''  # 结果概述缓存
        self.result = b''  # 结果内容缓存
        self.curFunc = 0  # 当前缓存结果对应功能

    @staticmethod
    def __get_addr_bytes(addr):
        lb = addr & 0xFF
        hb = (addr >> 8) & 0xFF
        return [lb, hb]

    def read_from_mem(self, addr, length):
        write = i2c_msg.write(self.i2c_addr, self.__get_addr_bytes(addr))
        read = i2c_msg.read(self.i2c_addr, length)
        with SMBus(self.i2c_adapter) as bus:
            bus.i2c_rdwr(write, read)
        return bytes(list(read))

    def write_to_mem(self, addr, dat):
        buf = self.__get_addr_bytes(addr)
        buf.extend(dat)
        write = i2c_msg.write(self.i2c_addr, buf)
        with SMBus(self.i2c_adapter) as bus:
            bus.i2c_rdwr(write)
            
    def firmware_version(self):
        """
            功能:  获取固件版本号
            参数:  无
            返回:  版本号字符串, 如: "v0.6.5"
        """
        return self.read_from_mem(WONDERCAM_REG_SYS_FIRMWARE_VERSION, 16).decode('utf-8').replace('\x00', '')

    def set_led(self, new_st):
        """
            功能:  设置LED开关状态
            参数:  new_st 新的LED状态, False为关, True为开
            返回: None
        """
        self.write_to_mem(WONDERCAM_REG_SYS_LIGHT_STATE,[new_st, ])

    def cur_func(self):
        """
            功能:  获取当前运行中的功能
            参数: fun 要运行的新功能
            返回: None
        """
        return int(self.read_from_mem(WONDERCAM_REG_SYS_CURRENT_FUNC, 1)[0])

    def set_func(self, func, timeout=3):
        """
            功能: 设置要运行的功能, 此方法会阻塞等待切换成功直至成功或超时
            参数: fun 要运行的新功能,  timeout超时时间单位秒
            返回: 成功返回True, 超时返回False
        """
        self.write_to_mem(WONDERCAM_REG_SYS_CURRENT_FUNC, [func, ])
        timeout += time.time()
        while True:
            time.sleep(0.05)
            if self.cur_func() == func:
                return True
            if time.time() > timeout:
                return False

    def update_result(self):
        """
            功能: 更新并获取结果到缓存
            参数: 无
            返回: None
        """
        cur_func = self.cur_func()
        self.curFunc = cur_func
        tmp = (None, 0x0400, 0x0800, 0x0C00, 0x0E00, 0x1000, 0x1400, 0x1E00, 0x1800, 0x1C00, 0x0D00, 0x0D80)
        addr = tmp[cur_func]
        if addr is None:
            return
        self.summary = self.read_from_mem(addr, 48)  # 获取结果概述,描述了本次结果的长度等情况
        if self.summary[1] > 0:  # 根据结果个数读取对应长度字节
            if 0 < cur_func < 3 or 5 <= cur_func < 7 or cur_func == 10 or cur_func == 11:
                self.result = self.read_from_mem(addr + 0x30, 16 * self.summary[1])
            elif cur_func == 7:
                self.result = self.read_from_mem(addr + 0x30, 32 * self.summary[1])
            else:  # 二维码等不直接读取,因为可能比较长, 再需要用时再读取
                self.result = b''
        else:
            self.result = b''

    def is_face_detected(self, id_want=None):  # 检查在画面中是否存在人脸
        """
            功能: 检查是否在画面中识别到指定ID的人脸
            参数: id_want在1~5间时代表要检查的人脸id, 为None就是为任意人脸, 若为0只指未学习的人脸
            返回: 若识别到则返回 True
                  若未识别到返回 False
        """
        if self.curFunc == WONDERCAM_FUNC_FACE_DETECT and self.summary[1] > 0:  # 当前结果的功能不是人脸识别
            if id_want is None:  # 任意人脸,已学习或未学习的
                return True
            if id_want == 0:
                id_want = 0xFF
            for i in range(4, 4 + self.summary[1]):  # 未学习or已学习人脸
                if self.summary[i] == id_want:
                    return True
        return False

    def get_face(self, id_want, face_type=1):  # 获取人脸, type = 1 为已学习人脸, type = 2 为未学习人脸
        """
          功能: 获取识别到的指定的人脸
          参数: id_want 要获取的人脸ID, 若face_type=2则为要获取的人脸的序号
                face_type 要获取的人脸的类型, face_type = 1为已学习人脸, face_type = 2 为未学习人脸
          返回: 若识别到则返回一个元组人脸的(中心X 中心Y, 宽, 高)
                若未识别到返回 None
        """
        if self.is_face_detected():
            if face_type == 1:  # 获取指定id人脸
                for i in range(4, 4 + self.summary[1]):  # 逐个检查识别到的人脸是否是想要的人脸
                    if self.summary[i] == id_want:
                        index = 16 * (i - 4)
                        return struct.unpack("<hhHH", self.result[index: index + 8])
            elif face_type == 2:  # 获取未学习人脸
                for i in range(4, 4 + self.summary[1]):
                    if self.summary[i] == 0xFF:
                        id_want -= 1  # 要第id_want 个未学习的人脸
                        if id_want == 0:
                            index = 16 * (i - 4)
                            return struct.unpack("<hhHH", self.result[index: index + 8])
            else:
                pass
        return None

    def is_detected_common(self, func, id_want):
        if self.curFunc == func and self.summary[1] > 0:
            if id_want is None:
                return True
            else:
                for i in range(2, 2 + self.summary[1]):
                    if self.summary[i] == id_want:
                        return True
        return False

    def is_object_detected(self, id_want=None):   # 人脸识别
        """
            功能: 检查是否在画面中识别到指定ID的人脸
            参数: id_want为None时检查是否识别到任何可以识别的物品, id_want为ID时检查是否识别到特定物品
            返回: 若识别到则返回 True
                 若未识别到返回 False
        """
        return self.is_detected_common(WONDERCAM_FUNC_OBJ_DETECT, id_want)

    def get_object(self, id_want, index):        # 图像分类
        """
            功能: 获取在画面中识别到的物品的数据
            参数: id_want为要获取的物品ID, index为要获取的物品序号(因可能在画面中同时识别到多个同样的物品)
            返回: (X坐标, Y坐标, 宽度, 高度)
        """
        if self.is_object_detected():
            for i in range(2, 2 + self.summary[1]):
                if self.summary[i] == id_want:
                    index -= 1
                    if index == 0:
                        index = 16 * (i - 2)
                        return struct.unpack("<hhHH", self.result[index: index + 8])
        return None

    def most_likely_id(self):              # 图像分类
        """
          功能: 图像分类或特征学习中获取可能性最大的ID
          参数: 无
          返回: 一个整数, 可能性最大的ID
        """
        if self.curFunc == WONDERCAM_FUNC_CLASSIFICATION or self.curFunc == WONDERCAM_FUNC_FEATURE_LEARNING:
            return self.summary[1]
        return 0

    def max_conf(self):
        """
          功能: 图像分类或特征学习中获取可能性最大的ID对应的概率
          参数: 无
          返回: 一个小数, 对应的概率
        """
        if self.curFunc == WONDERCAM_FUNC_CLASSIFICATION or self.curFunc == WONDERCAM_FUNC_FEATURE_LEARNING:
            conf = int.from_bytes(self.summary[2:4], byteorder="little", signed=False)
            conf = conf / 10000.0
            return conf
        return 0

    def conf_of_id(self, id_want):      
        """
          功能: 图像分类或特征学习中获取图像是指定id_want的概率
          参数: 无
          返回: 一个小数, 对应的概率
        """
        if self.curFunc == WONDERCAM_FUNC_CLASSIFICATION or self.curFunc == WONDERCAM_FUNC_FEATURE_LEARNING:
            addr = 0x10 + ((id_want - 1) * 4)
            conf = int.from_bytes(self.summary[addr: addr + 2], byteorder="little", signed=False)
            conf = conf / 10000.0
            return conf
        return 0

    def is_color_blob_detected(self, id_want=None):    # 颜色识别
        """
          功能: 检查是否在画面中识别到指定的颜色ID
          参数: id_want=要获取的颜色ID, id_want为None时检查任意一个ID的颜色, id_want为1~7间时检测指定ID颜色
          返回: 若识别到则返回 True
                若未识别到返回 False
        """
        return self.is_detected_common(WONDERCAM_FUNC_COLOR_DETECT, id_want)

    def get_color_blob(self, id_want):
        """
          功能: 获取识别到的指定ID的颜色色块位置数据
          参数: id_want=要获取的颜色ID
          返回: 若识别到则返回一个元组色块的(中心X 中心Y, 宽, 高)
                若未识别到返回 None
        """
        if self.is_color_blob_detected(id_want):
            for i in range(2, 2 + self.summary[1]):
                if self.summary[i] == id_want:
                    index = 16 * (i - 2)
                    return struct.unpack("<hhHH", self.result[index: index + 8])  # 反序列化
        return None

    def is_line_detected(self, id_want=None):        # 视觉巡线
        """
          功能: 检查是否在画面中识别到指定ID的线条
          参数: id_want=要获取的线条ID, id_want为None时检查任意一个ID的线条, id_want为1~3间时检测指定ID的线条
          返回: 若识别到则返回 True
                若未识别到返回 False
        """
        return self.is_detected_common(WONDERCAM_FUNC_LINE_FOLLOWING, id_want)

    def get_line(self, id_want):
        """
          功能: 获取识别到的指定ID的线条数据
          参数: id_want=要获取的线条ID
          返回: 若识别到则返回一个元组色块的(箭头尖端X, 简短Y, 尾部X, 尾部Y, 偏移, 角度)
                若未识别到返回 None
          关于apriltag标签识别, WonderCam Standard v1设计只识别TAG36H11族标签,其他族的apriltag会被忽略不识别      
        """
        if self.is_line_detected(id_want):
            for i in range(2, 2 + self.summary[1]):
                if self.summary[i] == id_want:
                    index = 16 * (i - 2)
                    x, y, w, h, angle, offset = struct.unpack("<hhHHhh", self.result[index: index + 12])
                    angle = angle - 180 if angle > 90 else angle
                    offset = abs(offset) - 160
                    return x, y, w, h, angle, offset
        return None

    def is_tag_detected(self, id_want=None):        # 标签识别
        """
          功能: 检查是否在画面中识别到了标签, 或者指定ID的标签
          参数: id_want 若为None则识别任意ID的标签, 若为指定的ID数值则识别指定ID
          返回:  若识别到则返回 True
                若未识别到返回 False
        """
        return self.is_detected_common(WONDERCAM_FUNC_APRILTAG, id_want)

    def num_of_tag_detected(self, id_want=None):
        """
          功能: 获取在画面中识别到的标签的个数
          参数: id_want 若为None则获取画面中识别到的全部标签的个数, 若为指定ID则获取画面中识别到的指定ID的标签个数
          返回:  若识别到则返回识别到的个数
                若未识别到返回0
        """
        ret = 0
        if self.curFunc == WONDERCAM_FUNC_APRILTAG:
            if id_want is None:
                return int(self.summary[1])
            else:
                if id_want >= 0:
                    for i in range(2, 2 + self.summary[1]):
                        if self.summary[i] == id_want:
                            ret += 1
        return ret

    def get_tag(self, id_want, index):
        """
          功能: 获取在画面中识别到的第index个id为id_want的标签的位置姿态数据
          参数: id_want为要获取的标签的id, index为画面中识别到的第index个id_want的标签 (一个画面中可以识别到多个同id的标签, 所以要做选择)
          返回: 若获取成功返回标签数据 (中心X, 中心Y, 宽, 高, X轴变换, X轴旋转, Y轴变换, Y轴旋转, Z轴变换, Z轴旋转)
        """
        if self.is_tag_detected():
            for i in range(2, 2 + self.summary[1]):
                if self.summary[i] == id_want:
                    index -= 1
                    if index == 0:
                        index = 32 * (i - 2)
                        return struct.unpack("<hhHHffffff", self.result[index: index + 32])
        return None

    def landmark_most_likely(self):         # 路标识别
        """
          功能: 获取路标识别中最可能的路标
          参数: 无
          返回: 一个整数, 可能性最大的路标的ID
        """
        if self.curFunc == WONDERCAM_FUNC_LANDMARK_REC:
            return self.summary[1]
        return 0

    def landmark_max_conf(self):
        """
          功能: 路标识别中最大的概率
          参数: 无
          返回: 一个小数, 对应的概率
        """
        if self.curFunc == WONDERCAM_FUNC_LANDMARK_REC:
            conf = int.from_bytes(self.summary[2:4], byteorder="little", signed=False)
            conf = conf / 10000.0
            return conf
        return 0

    def landmark_conf_of_id(self, id_want):
        """
          功能: 路标识别中是某个指定的路标的概率
          参数: 无
          返回: 一个小数, 对应的概率
        """
        if self.curFunc == WONDERCAM_FUNC_LANDMARK_REC:
            addr = 0x10 + ((id_want - 1) * 4)
            conf = int.from_bytes(self.summary[addr: addr + 2], byteorder="little", signed=False)
            conf = conf / 10000.0
            return conf
        return 0

    def number_most_likely(self):          # 数字识别
        """
          功能: 获取数字识别中最可能的路标
          参数: 无
          返回: 一个整数, 可能性最大的数字的ID
        """
        if self.curFunc == WONDERCAM_FUNC_NUMBER_REC:
            return self.summary[1]
        return 0

    def number_max_conf(self):
        """
          功能: 数字识别中最大的概率
          参数: 无
          返回: 一个小数, 对应的概率
        """
        if self.curFunc == WONDERCAM_FUNC_NUMBER_REC:
            conf = int.from_bytes(self.summary[2:4], byteorder="little", signed=False)
            conf = conf / 10000.0
            return conf
        return 3

if __name__ == "__main__":
    cam = WonderCam()
    # """识别指定线条"""
    cam.set_func(WONDERCAM_FUNC_LINE_FOLLOWING)
    while True:
        try:
            cam.update_result()
            cam.set_led(True)
            if cam.is_line_detected():
                for i in range(1,4):
                    if cam.get_line(i):  
                        print("识别到线条ID: %d" %i)   
                        print(cam.get_line(i))
            else:
                print("未识别到线条")
            time.sleep(0.5)
        except KeyboardInterrupt:
            break
        except Exception as e:
            print(e)
            continue
这是识别线条id的代码
最佳答案
2023-4-29 12:22:14
要让树莓派通过小幻熊AI视觉模块识别黑线并进行巡线,你需要编写一个Python程序来控制机器人的移动。你需要将视觉模块与树莓派连接,并使用Python代码来读取它的输出。

下面是一个简单的代码示例,用于识别黑线并控制机器人向前移动:
import time
import RPi.GPIO as GPIO
from hx711 import HX711
from pixy import *

# 初始化Pixy摄像头
pixy = Pixy()

# 初始化HX711模块
hx711 = HX711(dout_pin=5, pd_sck_pin=6)
hx711.set_scale_ratio(scale_ratio=2272.0)
hx711.reset()
hx711.tare()

# 初始化GPIO引脚
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(17, GPIO.OUT)
GPIO.setup(18, GPIO.OUT)

# 设置机器人移动的速度
speed = 40

# 循环执行
while True:
    # 读取Pixy摄像头的输出
    black_line_id = pixy.get_black_line_id()

    # 如果检测到黑线
    if black_line_id != 0:
        # 读取HX711模块的输出,计算重量
        weight = hx711.get_weight_mean()
        
        # 根据重量控制机器人的移动方向
        if weight < -10:
            GPIO.output(17, True)
            GPIO.output(18, False)
        elif weight > 10:
            GPIO.output(17, False)
            GPIO.output(18, True)
        else:
            GPIO.output(17, True)
            GPIO.output(18, True)
    # 如果未检测到黑线
    else:
        GPIO.output(17, False)
        GPIO.output(18, False)
    
    # 等待一段时间后继续执行
    time.sleep(0.01)
这个代码示例假设你已经安装了相关的Python模块,例如RPi.GPIO用于控制GPIO引脚,hx711用于读取重量传感器的数据,以及pixy用于读取Pixy摄像头的输出。

代码首先初始化了Pixy摄像头和HX711模块,并设置了GPIO引脚用于控制机器人的移动。然后进入一个循环,在循环中不断读取Pixy摄像头的输出,并根据输出来控制机器人的移动方向。

如果检测到黑线,则读取重量传感器的数据,并根据重量来控制机器人向左或向右移动。如果未检测到黑线,则停止机器人的移动。

请注意,这只是一个简单的示例程序,你需要根据你的具体硬件和代码实现进行适当的修改。同时,要确保在执行该程序之前正确连接所有的硬件设备,并安装必要的Python模块。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-4-26 20:55:58 | 显示全部楼层
你没有闭合[code]
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-4-26 21:05:31 | 显示全部楼层
不是很懂,就是想要识别到那个id的时候执行由多个舵机配合的行走,然后达到个自主巡线的功能
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-4-26 21:29:30 | 显示全部楼层
korone 发表于 2023-4-26 21:05
不是很懂,就是想要识别到那个id的时候执行由多个舵机配合的行走,然后达到个自主巡线的功能

看不懂你的代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-4-29 12:22:14 | 显示全部楼层    本楼为最佳答案   
要让树莓派通过小幻熊AI视觉模块识别黑线并进行巡线,你需要编写一个Python程序来控制机器人的移动。你需要将视觉模块与树莓派连接,并使用Python代码来读取它的输出。

下面是一个简单的代码示例,用于识别黑线并控制机器人向前移动:
import time
import RPi.GPIO as GPIO
from hx711 import HX711
from pixy import *

# 初始化Pixy摄像头
pixy = Pixy()

# 初始化HX711模块
hx711 = HX711(dout_pin=5, pd_sck_pin=6)
hx711.set_scale_ratio(scale_ratio=2272.0)
hx711.reset()
hx711.tare()

# 初始化GPIO引脚
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(17, GPIO.OUT)
GPIO.setup(18, GPIO.OUT)

# 设置机器人移动的速度
speed = 40

# 循环执行
while True:
    # 读取Pixy摄像头的输出
    black_line_id = pixy.get_black_line_id()

    # 如果检测到黑线
    if black_line_id != 0:
        # 读取HX711模块的输出,计算重量
        weight = hx711.get_weight_mean()
        
        # 根据重量控制机器人的移动方向
        if weight < -10:
            GPIO.output(17, True)
            GPIO.output(18, False)
        elif weight > 10:
            GPIO.output(17, False)
            GPIO.output(18, True)
        else:
            GPIO.output(17, True)
            GPIO.output(18, True)
    # 如果未检测到黑线
    else:
        GPIO.output(17, False)
        GPIO.output(18, False)
    
    # 等待一段时间后继续执行
    time.sleep(0.01)
这个代码示例假设你已经安装了相关的Python模块,例如RPi.GPIO用于控制GPIO引脚,hx711用于读取重量传感器的数据,以及pixy用于读取Pixy摄像头的输出。

代码首先初始化了Pixy摄像头和HX711模块,并设置了GPIO引脚用于控制机器人的移动。然后进入一个循环,在循环中不断读取Pixy摄像头的输出,并根据输出来控制机器人的移动方向。

如果检测到黑线,则读取重量传感器的数据,并根据重量来控制机器人向左或向右移动。如果未检测到黑线,则停止机器人的移动。

请注意,这只是一个简单的示例程序,你需要根据你的具体硬件和代码实现进行适当的修改。同时,要确保在执行该程序之前正确连接所有的硬件设备,并安装必要的Python模块。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-5-3 18:34:37 | 显示全部楼层
陶远航 发表于 2023-4-29 12:22
要让树莓派通过小幻熊AI视觉模块识别黑线并进行巡线,你需要编写一个Python程序来控制机器人的移动。你需要 ...

我是用的幻尔科技的LX-824的舵机摄像头好像不是pixy的是那个小幻熊ai视觉模块
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-9-23 11:21

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表