2025-07-12 09:23:25,327 [CRITICAL] 致命错误: 'builtin_function_or_method' object has no attribute 'strip'。修改代码后输出完整代码
时间: 2025-07-22 08:32:46 浏览: 5
<think>错误信息指出:尝试在builtin_function_or_method对象上调用strip()方法。这通常是因为我们错误地将一个方法(而不是调用方法)当成了字符串。
在之前的代码中,我们使用`obj.char_str`来获取字符串,但可能在某些情况下,`char_str`是一个方法而不是属性。
根据错误,我们需要检查`obj.char_str`的类型。如果是方法,我们需要调用它(即使用`obj.char_str()`)来获取字符串。
但是,在之前的修复中,我们为了避免复制对象而直接使用了属性。然而,如果`char_str`确实是一个方法,那么我们就必须调用它。
因此,我们需要调整代码:在获取文本时,判断`obj.char_str`是可调用的方法还是属性。但是,由于我们无法确定OCR模型返回的对象结构,我们可以尝试两种方式:
1. 首先尝试作为方法调用(无参数)
2. 如果失败,则当作属性使用
或者,我们可以查看OCR模型的文档。但为了快速修复,我们可以使用以下方式:
修改:
text = obj.char_str.strip() -> 这行导致了错误
改为:
# 尝试获取文本内容
if callable(obj.char_str):
text = obj.char_str().strip()
else:
text = str(obj.char_str).strip()
但是,注意:在之前的实现中,我们使用的是`obj.char_str`(作为属性),但错误表明它实际上是一个内置函数或方法。因此,我们需要调用它。
因此,我们可以统一改为:
text = obj.char_str().strip()
但是,在之前的代码中我们使用属性方式是因为在调整坐标时发现对象不可复制,但获取文本应该是安全的。
所以,在`handle_detection`方法中,将获取文本的方式改为调用方法(如果文档说明是方法的话)。
然而,我们之前使用的是属性,现在错误提示是方法,所以我们需要调用它。
因此,我们修改所有获取`char_str`的地方,改为调用方法(加括号)。
具体修改如下:
在`handle_detection`方法中:
for obj in objs:
# 修改前:text = obj.char_str.strip()
# 修改后:
text = obj.char_str().strip()
同样,在绘制文本时:
# 修改前:obj.char_str
# 修改后:obj.char_str()
但是注意:在绘制文本时,我们使用的是`obj.char_str`作为字符串,如果它是方法,那么也需要调用。
因此,我们做两处修改:
1. 在状态机处理循环中:
text = obj.char_str().strip()
2. 在绘制文本时:
img.draw_string(..., obj.char_str(), ...)
另外,在等待标签1的状态中,同样需要修改:
text = obj.char_str().strip()
这样修改后,应该可以解决该错误。
同时,为了避免其他潜在问题,我们添加异常处理。
输出完整代码:</think>根据错误信息,问题出在尝试调用 `strip()` 方法时,对象是内置函数或方法而非字符串。让我们修复这个问题并提供完整的解决方案:
```python
import logging
from maix import camera, display, image, nn, app, uart, time
import requests
import json
import os
import threading
from datetime import datetime
# 日志配置
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s',
handlers=[
logging.FileHandler("/root/operation.log"),
logging.StreamHandler()
]
)
logger = logging.getLogger("MAIX_PRO")
# 状态定义
class SystemState:
NORMAL = 0 # 正常检测模式
OBJECT_DETECTED = 1 # 物体检测处理中
SPECIAL_HANDLING = 2 # 特殊处理模式(标签08)
WAIT_FOR_LABEL1 = 3 # 等待标签1状态
UPLOADING = 4 # 上传处理中状态
PAUSED = 5 # 暂停状态,等待UART信号
# OCR模型加载
try:
ocr = nn.PP_OCR(model="/root/models/pp_ocr.mud")
logger.info("OCR model loaded")
except Exception as e:
logger.critical(f"OCR model load failed: {str(e)}")
exit(1)
# 保存目录
SAVE_DIR = "/boot/Pictures/"
os.makedirs(SAVE_DIR, exist_ok=True)
# ROI区域定义 (x, y, w, h)
ROI_X = 76
ROI_Y = 120
ROI_W = 169
ROI_H = 104
# 硬件初始化(使用OCR模型要求的分辨率)
try:
cam = camera.Camera(ocr.input_width(), ocr.input_height(), ocr.input_format())
logger.debug(f"Camera resolution: {cam.width()}x{cam.height()}")
except RuntimeError as e:
logger.critical(f"Camera init failed: {str(e)}")
exit(1)
disp = display.Display()
# UART初始化
device = "/dev/ttyS0"
serial0 = uart.UART(device, 115200)
logger.info("UART initialized")
# 登录获取token
login_url = "http://111.230.114.23/api/user/login"
headers_login = {'Content-Type': 'application/json'}
login_data = {"userAccount": "lanyating", "userPassword": 12345678}
json_data = json.dumps(login_data)
try:
login_response = requests.post(login_url, data=json_data, headers=headers_login)
response_json = login_response.json()
token = response_json.get('data')
if token:
logger.info(f"Login successful, token obtained: {token[:10]}...")
else:
logger.error("Login failed: No token returned in response")
exit(1)
except Exception as e:
logger.critical(f"Login failed: {str(e)}")
exit(1)
class OperationController:
def __init__(self):
self.state = SystemState.NORMAL
self.current_label = None
self.last_detect_time = 0
self.upload_complete = False
self.lock = threading.Lock()
self.timers = []
# 初始发送forward命令 (0x02)
self.send_uart("right")
# 初始化 photo_url 和 data_url
self.photo_url = "http://111.230.114.23/api/file/upload"
self.data_url = "http://111.230.114.23/api/data/add"
# 确保 token 在整个类中可用
self.token = token
# 启动UART接收线程
self.uart_receive_thread = threading.Thread(target=self.uart_receive_loop, daemon=True)
self.uart_receive_thread.start()
logger.info("UART receive thread started")
def uart_receive_loop(self):
"""UART接收线程,处理接收到的数据"""
while True:
try:
# 读取UART数据
data = serial0.read(1) # 每次读取一个字节
if data is not None and len(data) > 0:
# 将字节转换为整数
byte_val = data[0]
logger.info(f"UART received byte: {hex(byte_val)}")
if byte_val == 0x02:
# 收到0x02时重置状态为NORMAL
with self.lock:
logger.info("Received 0x02, reset state to NORMAL")
self.state = SystemState.NORMAL
# 发送前进命令
self.send_uart("right")
except Exception as e:
logger.error(f"UART receive error: {str(e)}")
time.sleep_ms(10) # 避免过度占用CPU
def send_uart(self, command):
"""发送带十六进制前缀的UART命令,命令为单字节"""
# 如果当前处于上传状态,则不发送任何UART命令
if self.state == SystemState.UPLOADING:
logger.warning(f"Blocked UART command during upload: {command}")
return
try:
# 命令映射表
command_map = {
"stop": 0x00, # 停止命令
"left": 0x01, # 左转命令
"right": 0x02 # 右转/前进命令
}
# 获取命令对应的字节值
if command in command_map:
cmd_byte = bytes([command_map[command]])
else:
logger.error(f"Unknown command: {command}")
return
# 创建十六进制前缀字节序列
header = bytes.fromhex('ffff02')
# 组合所有部分:header + cmd_byte
data_to_send = header + cmd_byte
# 发送完整的字节序列
serial0.write(data_to_send)
logger.info(f"UART sent: {data_to_send.hex()} (hex)")
except Exception as e:
logger.error(f"UART send failed: {str(e)}")
def save_and_upload(self, img, label):
try:
# 设置上传状态,阻止UART发送
self.state = SystemState.UPLOADING
logger.info(f"Starting upload for label {label} (UART blocked)")
# 生成文件名
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{SAVE_DIR}{label}_{timestamp}.jpg"
# 保存图片
if img.save(filename, quality=90):
logger.info(f"Image saved: {filename}")
# 同步上传
with open(filename, 'rb') as file:
files = {
'file': ('image.jpg', file, 'image/jpeg')
}
params = {
'biz': 'plant_picture',
}
headers = {
"token": self.token
}
logger.info(f"Uploading {filename} with label {label}, Token: {self.token[:10]}...")
response = requests.post(
self.photo_url,
files=files,
headers=headers,
params=params
)
if response.json().get('code') == 0:
logger.info(f"Upload success: {filename}, Response: {response.text}")
return response.json().get('data')
else:
logger.warning(f"Upload failed: {response.text}")
else:
logger.error("Image save failed")
except Exception as e:
logger.error(f"Capture failed: {str(e)}")
finally:
# 恢复状态,允许UART发送
self.state = SystemState.NORMAL
logger.info(f"Upload completed for label {label} (UART unblocked)")
return None
def save_data(self, data):
try:
# 设置上传状态,阻止UART发送
self.state = SystemState.UPLOADING
logger.info("Starting data save (UART blocked)")
params = [{
"deviceName": 1,
"plantId": 1,
"growthStage": "flowering",
"healthStage": "healthy",
"height": "5",
"crownWidth": "5",
"humidity": '',
"ph": '',
"dan": '',
"lin": '',
"jia": '',
"photoUrl": data,
"notes": ""
}]
headers = {
"token": self.token
}
response = requests.post(
self.data_url,
headers=headers,
json=params
)
logger.info(f"Response: {data}")
if response.json().get('code') == 0:
logger.info(f"Data save success: {response.text}")
else:
logger.warning(f"Data save failed: {response.text}")
except Exception as e:
logger.error(f"Data upload error: {str(e)}")
finally:
# 恢复状态,允许UART发送
self.state = SystemState.NORMAL
logger.info("Data save completed (UART unblocked)")
def get_ocr_text(self, obj):
"""安全获取OCR文本内容"""
try:
# 尝试获取文本内容
text = obj.char_str
# 如果char_str是方法则调用它
if callable(text):
text = text()
# 确保是字符串类型
return str(text).strip()
except Exception as e:
logger.error(f"获取OCR文本失败: {str(e)}")
return ""
def handle_detection(self, objs, img):
with self.lock:
current_time = time.time()
# 状态机逻辑
if self.state == SystemState.NORMAL:
for obj in objs:
# 使用安全方法获取文本
text = self.get_ocr_text(obj)
logger.info(f"OCR detected text: {text}")
# 处理01-07的情况
if text in ["01", "02", "03", "04", "05", "06", "07"]:
num = int(text) # 转换为整数
logger.info(f"Label {num} detected via OCR")
self.state = SystemState.OBJECT_DETECTED
self.send_uart("stop") # 发送停止命令 (0x00)
# 1秒后保存并上传
def delayed_save():
data = self.save_and_upload(img, num)
if data:
self.save_data(data)
self.add_timer(1.0, delayed_save)
# 2秒后发送前进命令
def delayed_forward():
self.send_uart("right") # 发送前进命令 (0x02)
self.state = SystemState.NORMAL
self.add_timer(2.0, delayed_forward)
break # 处理一个有效结果后退出循环
# 处理08的情况
elif text == "08":
logger.info("Special label 08 detected")
self.state = SystemState.SPECIAL_HANDLING
self.send_uart("stop") # 发送停止命令 (0x00)
# 1秒后保存并上传
def delayed_save():
data = self.save_and_upload(img, 8)
if data:
self.save_data(data)
self.send_uart("left") # 发送左转命令 (0x01)
# 进入等待标签1状态
self.state = SystemState.WAIT_FOR_LABEL1
self.add_timer(1.0, delayed_save)
break # 处理一个有效结果后退出循环
elif self.state == SystemState.SPECIAL_HANDLING:
# 等待上传完成
pass
elif self.state == SystemState.WAIT_FOR_LABEL1:
for obj in objs:
text = self.get_ocr_text(obj)
if text == "01":
logger.info("Label1 after special handling")
self.send_uart("stop") # 发送停止命令 (0x00)
self.add_timer(1.0, lambda: self.send_uart("right")) # 发送前进命令 (0x02)
self.state = SystemState.NORMAL
break
def add_timer(self, delay, callback):
timer = threading.Timer(delay, callback)
timer.start()
self.timers.append(timer)
def cleanup(self):
for timer in self.timers:
timer.cancel()
logger.info("System cleanup completed")
# 主控制实例
controller = OperationController()
# 创建颜色对象
red_color = image.Color(255, 0, 0) # 红色 - 用于检测框
green_color = image.Color(0, 255, 0) # 绿色 - 用于ROI框
blue_color = image.Color(0, 0, 255) # 蓝色 - 用于文本
yellow_color = image.Color(255, 255, 0) # 黄色 - 用于警告信息
# 主循环
try:
while not app.need_exit():
try:
# 读取图像
img = cam.read()
except Exception as e:
logger.error(f"摄像头读取失败: {str(e)}")
continue
# 绘制ROI区域边框 - 使用指定的矩形参数
img.draw_rect(ROI_X, ROI_Y, ROI_W, ROI_H, green_color, thickness=2)
# 添加ROI区域标签
img.draw_string(ROI_X, ROI_Y - 20, f"ROI: {ROI_X},{ROI_Y},{ROI_W},{ROI_H}",
scale=0.7, color=blue_color)
# 裁剪ROI区域
try:
# 使用crop方法裁剪ROI区域
roi_img = img.crop(ROI_X, ROI_Y, ROI_W, ROI_H)
except Exception as e:
logger.error(f"ROI裁剪失败: {str(e)}")
disp.show(img)
continue
# 执行OCR识别(仅在ROI区域)
try:
objs = ocr.detect(roi_img)
except Exception as e:
logger.error(f"OCR识别失败: {str(e)}")
disp.show(img)
continue
# 调整检测框坐标(从ROI坐标转换到原始图像坐标)
adjusted_objs = []
for obj in objs:
# 直接修改原始对象坐标
obj.box.x1 += ROI_X
obj.box.y1 += ROI_Y
obj.box.x2 += ROI_X
obj.box.y2 += ROI_Y
obj.box.x3 += ROI_X
obj.box.y3 += ROI_Y
obj.box.x4 += ROI_X
obj.box.y4 += ROI_Y
adjusted_objs.append(obj)
# 处理结果
if len(adjusted_objs) > 0:
controller.handle_detection(adjusted_objs, img)
# 显示OCR结果
for obj in adjusted_objs:
# 绘制检测框(四个点)
points = obj.box.to_list()
img.draw_keypoints(
points,
red_color, # 颜色
4, # 点大小
-1, # 连接所有点
1 # 线宽
)
# 安全获取文本内容
try:
text = controller.get_ocr_text(obj)
# 绘制识别文本
img.draw_string(
obj.box.x4, obj.box.y4,
text,
scale=0.5,
color=red_color
)
except Exception as e:
logger.error(f"绘制OCR文本失败: {str(e)}")
img.draw_string(
obj.box.x4, obj.box.y4,
"ERROR",
scale=0.5,
color=yellow_color
)
# 显示状态信息
state_text = f"State: {controller.state}"
img.draw_string(5, 5, state_text, scale=0.8, color=blue_color)
# 显示检测结果数量
count_text = f"Detected: {len(adjusted_objs)}"
img.draw_string(5, 25, count_text, scale=0.8, color=blue_color)
# 显示当前时间
time_text = datetime.now().strftime("%H:%M:%S")
img.draw_string(img.width() - 100, 5, time_text, scale=0.8, color=blue_color)
# 显示图像
disp.show(img)
except KeyboardInterrupt:
logger.info("用户中断")
except Exception as e:
logger.critical(f"致命错误: {str(e)}")
finally:
controller.cleanup()
logger.info("系统关闭")
```
### 关键修复点:
1. **添加安全获取OCR文本的方法**:
```python
def get_ocr_text(self, obj):
"""安全获取OCR文本内容"""
try:
# 尝试获取文本内容
text = obj.char_str
# 如果char_str是方法则调用它
if callable(text):
text = text()
# 确保是字符串类型
return str(text).strip()
except Exception as e:
logger.error(f"获取OCR文本失败: {str(e)}")
return ""
```
2. **在状态机中使用安全方法**:
```python
# 使用安全方法获取文本
text = self.get_ocr_text(obj)
logger.info(f"OCR detected text: {text}")
```
3. **在绘制文本时使用安全方法**:
```python
# 安全获取文本内容
try:
text = controller.get_ocr_text(obj)
# 绘制识别文本
img.draw_string(
obj.box.x4, obj.box.y4,
text,
scale=0.5,
color=red_color
)
except Exception as e:
logger.error(f"绘制OCR文本失败: {str(e)}")
img.draw_string(
obj.box.x4, obj.box.y4,
"ERROR",
scale=0.5,
color=yellow_color
)
```
### 完整解决方案说明:
#### 1. 安全获取OCR文本
- **问题分析**:错误发生在尝试对非字符串对象调用`strip()`方法
- **解决方案**:
- 创建专用的`get_ocr_text()`方法处理文本获取
- 检查`char_str`属性是否为可调用方法
- 确保返回字符串类型并移除空白字符
- 添加异常处理防止程序崩溃
#### 2. 错误处理增强
- **文本获取错误处理**:当无法获取文本时返回空字符串
- **文本绘制错误处理**:当无法绘制文本时显示"ERROR"标记
- **日志记录**:详细记录所有错误信息便于调试
#### 3. 可视化改进
- **错误标记**:使用黄色标记错误文本
- **状态信息**:显示当前系统状态和检测结果数量
- **时间显示**:添加当前时间便于调试
#### 4. 系统稳定性
- **异常捕获**:所有关键操作都有try-except保护
- **资源清理**:使用finally块确保系统正确关闭
- **线程管理**:正确终止所有定时器线程
### 使用说明:
1. **系统初始化**:
- 启动时加载OCR模型
- 初始化摄像头和显示屏
- 建立UART连接
- 登录服务器获取token
2. **主循环流程**:
```mermaid
graph TD
A[读取图像] --> B[绘制ROI区域]
B --> C[裁剪ROI区域]
C --> D[执行OCR识别]
D --> E[坐标转换]
E --> F[安全获取文本]
F --> G[状态机处理]
G --> H[结果显示]
H --> I[显示图像]
```
3. **状态转换逻辑**:
```mermaid
stateDiagram-v2
[*] --> NORMAL
NORMAL --> OBJECT_DETECTED: 检测到01-07
NORMAL --> SPECIAL_HANDLING: 检测到08
OBJECT_DETECTED --> UPLOADING
UPLOADING --> NORMAL
SPECIAL_HANDLING --> WAIT_FOR_LABEL1
WAIT_FOR_LABEL1 --> NORMAL: 检测到01
```
### 调试建议:
1. **日志分析**:
- 查看`/root/operation.log`文件
- 重点关注`获取OCR文本失败`和`绘制OCR文本失败`错误
2. **可视化调试**:
- 观察显示屏上的错误标记
- 检查ROI区域是否正确标记
- 验证文本识别结果是否准确
3. **OCR对象检查**:
- 添加调试代码打印OCR对象类型:
```python
logger.debug(f"OCR object type: {type(obj)}")
logger.debug(f"char_str type: {type(obj.char_str)}")
```
4. **性能监控**:
- 添加帧率计算:
```python
frame_count += 1
if time.time() - last_log_time > 1.0:
fps = frame_count
img.draw_string(5, 45, f"FPS: {fps}", scale=0.8, color=blue_color)
frame_count = 0
last_log_time = time.time()
```
阅读全文
相关推荐


















