Módulo 3.2: Visão Computacional Avançada
👁️ Módulo 3.2: Visão Computacional Avançada
Dê olhos ao seu robô: percepção visual para robótica!
📋 Informações do Módulo
| Informação | Detalhes |
|---|---|
| Duração estimada | 15-20 horas |
| Nível | Avançado |
| Pré-requisitos | Python, RL básico, álgebra linear |
| Ferramentas | OpenCV, PyTorch, YOLO, MediaPipe, ROS 2 |
🎯 Objetivos de Aprendizado
Ao completar este módulo, você será capaz de:
- Processar imagens em tempo real para robótica
- Implementar detecção de objetos com YOLO
- Realizar segmentação semântica e de instâncias
- Estimar poses humanas e de objetos
- Integrar visão computacional com controle de robôs
- Treinar modelos de visão personalizados
- Lidar com o Sim2Real gap em visão
📚 Conteúdo Teórico
1. Fundamentos de Visão Computacional para Robótica
Por que Visão é Crítica para Robôs?
Robôs humanoides precisam perceber o mundo para:
- 🎯 Manipulação: Pegar objetos desconhecidos
- 🚶 Navegação: Evitar obstáculos dinâmicos
- 🤝 Interação: Detectar pessoas e gestos
- 🎭 Imitação: Aprender observando humanos
Diferença: Visão para Robótica vs. Computer Vision tradicional
| Aspecto | CV Tradicional | CV para Robótica |
|---|---|---|
| Latência | Segundos OK | < 50ms crítico |
| Precisão | 99%+ necessário | 90% + robustez OK |
| Ambiente | Controlado | Iluminação variável |
| Output | Labels/bboxes | Coordenadas 3D |
| Integração | Standalone | ROS 2 + controle |
Pipeline Clássico de Visão Robótica
Câmera → Pré-processamento → Detecção/Segmentação →
Transformação 3D → Planejamento → Controle
Exemplo: Pegar uma caneca
import cv2
import numpy as np
def robot_pick_pipeline(image, camera_intrinsics, robot):
"""Pipeline completo de visão para manipulação"""
# 1. PRÉ-PROCESSAMENTO
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
image_normalized = image_rgb / 255.0
# 2. DETECÇÃO DE OBJETOS
detections = detect_objects(image_normalized) # YOLO
mug_bbox = [d for d in detections if d['class'] == 'mug'][0]
# 3. SEGMENTAÇÃO (opcional, para grasp preciso)
mask = segment_object(image, mug_bbox)
# 4. ESTIMAÇÃO DE POSE 3D
depth_map = get_depth(image) # De câmera RGB-D ou estéreo
point_cloud = depth_to_pointcloud(depth_map, camera_intrinsics)
mug_pose_3d = estimate_3d_pose(point_cloud, mask)
# 5. TRANSFORMAÇÃO PARA FRAME DO ROBÔ
mug_pose_robot_frame = transform_camera_to_robot(
mug_pose_3d,
robot.camera_transform
)
# 6. PLANEJAMENTO DE GRASP
grasp_pose = plan_grasp(mug_pose_robot_frame, mug_shape='cylinder')
# 7. EXECUÇÃO
robot.move_to(grasp_pose)
robot.close_gripper()
return True
2. Processamento de Imagens Avançado
Calibração de Câmera
Antes de qualquer coisa, calibre sua câmera para ter métricas precisas.
import cv2
import numpy as np
import glob
def calibrate_camera(images_path):
"""
Calibra câmera usando padrão de xadrez
"""
# Preparar pontos do mundo real (0,0,0), (1,0,0), ...
pattern_size = (9, 6) # Cantos internos do xadrez
square_size = 0.025 # 25mm por quadrado
objp = np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2)
objp *= square_size
# Arrays para armazenar pontos
objpoints = [] # Pontos 3D no mundo real
imgpoints = [] # Pontos 2D na imagem
# Processar imagens de calibração
images = glob.glob(f'{images_path}/*.jpg')
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Encontrar cantos do xadrez
ret, corners = cv2.findChessboardCorners(gray, pattern_size, None)
if ret:
objpoints.append(objp)
# Refinar localização dos cantos
corners_refined = cv2.cornerSubPix(
gray, corners, (11, 11), (-1, -1),
criteria=(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
)
imgpoints.append(corners_refined)
# Calibrar câmera
ret, camera_matrix, dist_coeffs, rvecs, tvecs = cv2.calibrateCamera(
objpoints, imgpoints, gray.shape[::-1], None, None
)
print(f"Matriz da câmera:\n{camera_matrix}")
print(f"Coeficientes de distorção:\n{dist_coeffs}")
return camera_matrix, dist_coeffs
# Usar calibração para undistort imagens
camera_matrix, dist_coeffs = calibrate_camera('./calibration_images/')
def undistort_image(image, camera_matrix, dist_coeffs):
h, w = image.shape[:2]
new_camera_matrix, roi = cv2.getOptimalNewCameraMatrix(
camera_matrix, dist_coeffs, (w, h), 1, (w, h)
)
undistorted = cv2.undistort(image, camera_matrix, dist_coeffs, None, new_camera_matrix)
return undistorted
Segmentação e Tracking
Segmentação de cor (simples, mas efetivo):
def segment_by_color(image, lower_hsv, upper_hsv):
"""
Segmenta objetos por cor (ex: bola vermelha)
"""
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, lower_hsv, upper_hsv)
# Operações morfológicas para limpar ruído
kernel = np.ones((5, 5), np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
# Encontrar contornos
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(contours) > 0:
# Maior contorno = objeto de interesse
largest_contour = max(contours, key=cv2.contourArea)
x, y, w, h = cv2.boundingRect(largest_contour)
center = (x + w//2, y + h//2)
return mask, center
return mask, None
# Exemplo: Detectar bola vermelha
lower_red = np.array([0, 100, 100])
upper_red = np.array([10, 255, 255])
mask, center = segment_by_color(image, lower_red, upper_red)
Tracking de objetos (KCF Tracker):
import cv2
# Inicializar tracker
tracker = cv2.TrackerKCF_create()
# Primeiro frame: selecionar objeto
ret, frame = cap.read()
bbox = cv2.selectROI("Frame", frame, False)
tracker.init(frame, bbox)
# Loop de tracking
while True:
ret, frame = cap.read()
if not ret:
break
# Atualizar tracker
success, bbox = tracker.update(frame)
if success:
x, y, w, h = [int(v) for v in bbox]
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
else:
cv2.putText(frame, "Tracking falhou", (10, 50),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
cv2.imshow("Tracking", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
Transformações Geométricas 3D
Converter pontos da imagem 2D para coordenadas 3D no mundo real:
def pixel_to_3d_point(u, v, depth, camera_matrix):
"""
Converte pixel (u, v) com profundidade para ponto 3D
Parâmetros:
- u, v: coordenadas do pixel
- depth: profundidade em metros
- camera_matrix: matriz intrínseca da câmera
"""
fx = camera_matrix[0, 0]
fy = camera_matrix[1, 1]
cx = camera_matrix[0, 2]
cy = camera_matrix[1, 2]
# Fórmula de projeção inversa
x = (u - cx) * depth / fx
y = (v - cy) * depth / fy
z = depth
return np.array([x, y, z])
# Exemplo de uso
camera_matrix = np.array([
[500, 0, 320],
[0, 500, 240],
[0, 0, 1]
])
# Objeto detectado no pixel (320, 240) com profundidade 1.5m
point_3d = pixel_to_3d_point(320, 240, 1.5, camera_matrix)
print(f"Posição 3D: {point_3d}") # [0, 0, 1.5] (centro da imagem)
3. Detecção de Objetos com YOLO
O que é YOLO?
YOLO (You Only Look Once) é a arquitetura state-of-the-art para detecção de objetos em tempo real.
Vantagens para robótica:
- ⚡ Rápido: 30-60 FPS em GPU
- 🎯 Preciso: mAP > 50% no COCO
- 🔧 Flexível: Fácil de treinar em datasets customizados
Paper original: YOLOv1 (Redmon et al., 2016) Versão moderna: YOLOv8 (Ultralytics, 2023)
Instalação e Uso Rápido
pip install ultralytics
from ultralytics import YOLO
import cv2
# 1. Carregar modelo pré-treinado
model = YOLO('yolov8n.pt') # n=nano (rápido), s=small, m=medium, l=large
# 2. Detecção em imagem
image = cv2.imread('scene.jpg')
results = model(image)
# 3. Processar resultados
for result in results:
boxes = result.boxes # Bounding boxes
for box in boxes:
# Coordenadas
x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
# Confiança
conf = box.conf[0].cpu().item()
# Classe
cls = int(box.cls[0].cpu().item())
class_name = model.names[cls]
# Desenhar
cv2.rectangle(image, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2)
cv2.putText(image, f"{class_name} {conf:.2f}",
(int(x1), int(y1)-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
cv2.imshow('Detections', image)
cv2.waitKey(0)
Detecção em Tempo Real (Webcam/ROS)
import cv2
from ultralytics import YOLO
def real_time_detection():
model = YOLO('yolov8n.pt')
cap = cv2.VideoCapture(0) # Webcam
while True:
ret, frame = cap.read()
if not ret:
break
# Inferência
results = model(frame, verbose=False)
# Visualizar
annotated_frame = results[0].plot()
cv2.imshow('YOLOv8 Real-Time', annotated_frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
real_time_detection()
Treinar YOLO em Dataset Customizado
Caso de uso: Detectar peças específicas de robô
1. Preparar dataset (formato YOLO):
dataset/
├── images/
│ ├── train/
│ │ ├── img1.jpg
│ │ └── img2.jpg
│ └── val/
│ ├── img3.jpg
│ └── img4.jpg
└── labels/
├── train/
│ ├── img1.txt
│ └── img2.txt
└── val/
├── img3.txt
└── img4.txt
Formato de label (img1.txt):
0 0.5 0.5 0.3 0.4
1 0.7 0.3 0.2 0.2
(class_id, x_center, y_center, width, height) - normalizado [0-1]
2. Criar arquivo de configuração (data.yaml):
path: ./dataset
train: images/train
val: images/val
names:
0: gear
1: screw
2: motor
3. Treinar:
from ultralytics import YOLO
# Carregar modelo base
model = YOLO('yolov8n.pt')
# Fine-tuning
results = model.train(
data='data.yaml',
epochs=100,
imgsz=640,
batch=16,
lr0=0.01,
device=0, # GPU
project='robot_parts_detector',
name='exp1'
)
# Validar
metrics = model.val()
print(f"mAP50: {metrics.box.map50}")
print(f"mAP50-95: {metrics.box.map}")
# Exportar para produção
model.export(format='onnx') # Ou 'torchscript', 'tflite'
4. Usar modelo treinado:
model = YOLO('robot_parts_detector/exp1/weights/best.pt')
results = model('test_image.jpg')
YOLO + Depth para Localização 3D
import numpy as np
import cv2
from ultralytics import YOLO
class ObjectLocalizer3D:
def __init__(self, yolo_model_path, camera_matrix):
self.model = YOLO(yolo_model_path)
self.camera_matrix = camera_matrix
def detect_and_localize(self, rgb_image, depth_image):
"""
Detecta objetos e retorna posições 3D
"""
results = self.model(rgb_image)
objects_3d = []
for box in results[0].boxes:
x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
cls = int(box.cls[0])
conf = box.conf[0].cpu().item()
# Centro da bbox
u = int((x1 + x2) / 2)
v = int((y1 + y2) / 2)
# Profundidade média na região
depth_roi = depth_image[int(y1):int(y2), int(x1):int(x2)]
depth = np.median(depth_roi)
# Converter para 3D
point_3d = pixel_to_3d_point(u, v, depth, self.camera_matrix)
objects_3d.append({
'class': self.model.names[cls],
'confidence': conf,
'bbox_2d': (x1, y1, x2, y2),
'position_3d': point_3d
})
return objects_3d
# Uso
localizer = ObjectLocalizer3D('yolov8n.pt', camera_matrix)
objects = localizer.detect_and_localize(rgb_frame, depth_frame)
for obj in objects:
print(f"{obj['class']} at {obj['position_3d']} with conf {obj['confidence']:.2f}")
4. Segmentação Semântica e de Instâncias
Diferença entre Segmentação
- Semântica: Classifica cada pixel (ex: “pessoa”, “cadeira”)
- Instâncias: Separa objetos individuais (ex: “pessoa 1”, “pessoa 2”)
Segmentação com YOLOv8-seg
from ultralytics import YOLO
# Modelo com segmentação
model = YOLO('yolov8n-seg.pt')
# Inferência
results = model('image.jpg')
# Acessar máscaras
for result in results:
masks = result.masks # Máscaras de segmentação
boxes = result.boxes
for i, (mask, box) in enumerate(zip(masks.data, boxes)):
# Máscara binária
mask_np = mask.cpu().numpy()
# Classe
cls = int(box.cls[0])
class_name = model.names[cls]
# Salvar máscara
cv2.imwrite(f'mask_{i}_{class_name}.png', mask_np * 255)
Segmentação Avançada com Detectron2
Detectron2 (Facebook AI) oferece modelos state-of-the-art:
pip install 'git+https://github.com/facebookresearch/detectron2.git'
from detectron2 import model_zoo
from detectron2.engine import DefaultPredictor
from detectron2.config import get_cfg
from detectron2.utils.visualizer import Visualizer
from detectron2.data import MetadataCatalog
import cv2
# Configurar
cfg = get_cfg()
cfg.merge_from_file(model_zoo.get_config_file("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml"))
cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.5
cfg.MODEL.WEIGHTS = model_zoo.get_checkpoint_url("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml")
predictor = DefaultPredictor(cfg)
# Inferência
im = cv2.imread("input.jpg")
outputs = predictor(im)
# Visualizar
v = Visualizer(im[:, :, ::-1], MetadataCatalog.get(cfg.DATASETS.TRAIN[0]), scale=1.2)
out = v.draw_instance_predictions(outputs["instances"].to("cpu"))
cv2.imshow('Segmentation', out.get_image()[:, :, ::-1])
cv2.waitKey(0)
# Acessar dados
instances = outputs["instances"]
masks = instances.pred_masks # torch.Tensor (N, H, W)
boxes = instances.pred_boxes
classes = instances.pred_classes
scores = instances.scores
Aplicação: Grasp Planning com Segmentação
def plan_grasp_from_segmentation(rgb_image, depth_image, target_class='cup'):
"""
Usa segmentação para planejar grasp em objeto específico
"""
# 1. Segmentar objetos
outputs = predictor(rgb_image)
instances = outputs["instances"]
# 2. Filtrar classe desejada
target_idx = [i for i, cls in enumerate(instances.pred_classes)
if model_metadata.thing_classes[cls] == target_class]
if not target_idx:
return None
# 3. Pegar primeira instância da classe
idx = target_idx[0]
mask = instances.pred_masks[idx].cpu().numpy()
# 4. Calcular centroide da máscara
y_coords, x_coords = np.where(mask)
centroid_x = int(np.mean(x_coords))
centroid_y = int(np.mean(y_coords))
# 5. Obter profundidade
depth = depth_image[centroid_y, centroid_x]
# 6. Converter para 3D
grasp_point_3d = pixel_to_3d_point(centroid_x, centroid_y, depth, camera_matrix)
# 7. Estimar orientação (usando PCA nos pontos da máscara)
points_3d = []
for y, x in zip(y_coords[::10], x_coords[::10]): # Subsample
d = depth_image[y, x]
p = pixel_to_3d_point(x, y, d, camera_matrix)
points_3d.append(p)
points_3d = np.array(points_3d)
# PCA para encontrar eixo principal
from sklearn.decomposition import PCA
pca = PCA(n_components=3)
pca.fit(points_3d)
principal_axis = pca.components_[0] # Eixo principal
return {
'position': grasp_point_3d,
'orientation': principal_axis,
'mask': mask
}
5. Pose Estimation (Humanos e Objetos)
Estimação de Pose Humana com MediaPipe
MediaPipe (Google) oferece pose estimation em tempo real:
pip install mediapipe
import mediapipe as mp
import cv2
mp_pose = mp.solutions.pose
mp_drawing = mp.solutions.drawing_utils
# Inicializar
pose = mp_pose.Pose(
static_image_mode=False,
model_complexity=1,
smooth_landmarks=True,
min_detection_confidence=0.5,
min_tracking_confidence=0.5
)
cap = cv2.VideoCapture(0)
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
# Converter para RGB
image_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# Processar
results = pose.process(image_rgb)
# Desenhar landmarks
if results.pose_landmarks:
mp_drawing.draw_landmarks(
frame,
results.pose_landmarks,
mp_pose.POSE_CONNECTIONS
)
# Acessar coordenadas
landmarks = results.pose_landmarks.landmark
left_wrist = landmarks[mp_pose.PoseLandmark.LEFT_WRIST]
print(f"Punho esquerdo: x={left_wrist.x}, y={left_wrist.y}, z={left_wrist.z}")
cv2.imshow('Pose Estimation', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
Aplicação: Robô Imitando Gestos
class GestureImitator:
def __init__(self, robot_controller):
self.pose = mp_pose.Pose()
self.robot = robot_controller
def track_and_imitate(self, frame):
"""
Detecta pose humana e replica no robô
"""
image_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
results = self.pose.process(image_rgb)
if not results.pose_landmarks:
return
landmarks = results.pose_landmarks.landmark
# Mapear ombro e cotovelo
left_shoulder = landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER]
left_elbow = landmarks[mp_pose.PoseLandmark.LEFT_ELBOW]
left_wrist = landmarks[mp_pose.PoseLandmark.LEFT_WRIST]
# Calcular ângulo do cotovelo
angle = self.calculate_angle(left_shoulder, left_elbow, left_wrist)
# Enviar para robô
self.robot.set_joint_angle('left_elbow', angle)
def calculate_angle(self, a, b, c):
"""Calcula ângulo ABC"""
import math
radians = math.atan2(c.y - b.y, c.x - b.x) - math.atan2(a.y - b.y, a.x - b.x)
angle = np.abs(radians * 180.0 / np.pi)
if angle > 180.0:
angle = 360 - angle
return angle
Pose Estimation de Objetos (6D Pose)
Para manipulação robótica precisa, precisamos estimar 6DOF pose (3D posição + 3D orientação):
# Usando PVNet ou similar (exemplo conceitual)
def estimate_6d_pose(rgb_image, object_model):
"""
Estima pose 6D de objeto conhecido
"""
# 1. Detectar objeto (YOLO)
bbox = detect_object(rgb_image, object_class)
# 2. Extrair keypoints (rede neural treinada)
keypoints_2d = extract_keypoints(rgb_image, bbox)
# 3. Resolver PnP (Perspective-n-Point)
# Mapear keypoints 2D para 3D usando modelo CAD
success, rvec, tvec = cv2.solvePnP(
object_model.keypoints_3d,
keypoints_2d,
camera_matrix,
dist_coeffs
)
if success:
# rvec: rotation vector, tvec: translation vector
rotation_matrix, _ = cv2.Rodrigues(rvec)
pose_6d = {
'position': tvec.flatten(),
'rotation': rotation_matrix
}
return pose_6d
return None
6. Integração Visão + Reinforcement Learning
Observações Visuais em RL
Usar imagens como entrada para políticas de RL:
import torch
import torch.nn as nn
from stable_baselines3 import PPO
from stable_baselines3.common.torch_layers import BaseFeaturesExtractor
class CNNFeatureExtractor(BaseFeaturesExtractor):
"""
CNN para extrair features de imagens para RL
"""
def __init__(self, observation_space, features_dim=256):
super().__init__(observation_space, features_dim)
n_input_channels = observation_space.shape[0]
self.cnn = nn.Sequential(
nn.Conv2d(n_input_channels, 32, kernel_size=8, stride=4),
nn.ReLU(),
nn.Conv2d(32, 64, kernel_size=4, stride=2),
nn.ReLU(),
nn.Conv2d(64, 64, kernel_size=3, stride=1),
nn.ReLU(),
nn.Flatten(),
)
# Calcular dimensão de saída
with torch.no_grad():
sample = torch.zeros(1, *observation_space.shape)
n_flatten = self.cnn(sample).shape[1]
self.linear = nn.Sequential(
nn.Linear(n_flatten, features_dim),
nn.ReLU()
)
def forward(self, observations):
return self.linear(self.cnn(observations))
# Usar com PPO
policy_kwargs = dict(
features_extractor_class=CNNFeatureExtractor,
features_extractor_kwargs=dict(features_dim=256)
)
model = PPO(
"CnnPolicy",
env,
policy_kwargs=policy_kwargs,
verbose=1
)
Ambiente com Observações Visuais
import gymnasium as gym
from gymnasium import spaces
import numpy as np
class VisualPickEnv(gym.Env):
"""
Ambiente de pick-and-place com observações visuais
"""
def __init__(self, render_mode='rgb_array'):
super().__init__()
# Observação: imagem RGB 84x84x3
self.observation_space = spaces.Box(
low=0, high=255,
shape=(3, 84, 84),
dtype=np.uint8
)
# Ação: [dx, dy, dz, gripper]
self.action_space = spaces.Box(
low=-1, high=1,
shape=(4,),
dtype=np.float32
)
def reset(self, seed=None):
# Setup simulação
self.setup_scene()
# Render primeira imagem
obs = self.render_observation()
return obs, {}
def step(self, action):
# Executar ação
self.apply_action(action)
# Render nova imagem
obs = self.render_observation()
reward = self.compute_reward()
done = self.check_done()
return obs, reward, done, False, {}
def render_observation(self):
"""Renderiza câmera e retorna imagem processada"""
# Capturar imagem da câmera simulada
image = self.camera.get_image() # (H, W, 3)
# Resize para 84x84
image = cv2.resize(image, (84, 84))
# Normalizar e transpor para (C, H, W)
image = image.transpose(2, 0, 1)
return image
Domain Randomization para Sim2Real
Problema: Modelos treinados em simulação falham no mundo real.
Solução: Randomizar aparência visual na simulação para forçar robustez.
import numpy as np
import cv2
class DomainRandomization:
"""
Aplica randomização visual em imagens de treino
"""
def __init__(self):
pass
def randomize(self, image):
"""
Aplica múltiplas randomizações
"""
image = self.randomize_brightness(image)
image = self.randomize_contrast(image)
image = self.randomize_hue(image)
image = self.add_noise(image)
image = self.randomize_blur(image)
return image
def randomize_brightness(self, image):
"""Varia brilho"""
factor = np.random.uniform(0.5, 1.5)
return np.clip(image * factor, 0, 255).astype(np.uint8)
def randomize_contrast(self, image):
"""Varia contraste"""
factor = np.random.uniform(0.5, 1.5)
mean = np.mean(image)
return np.clip((image - mean) * factor + mean, 0, 255).astype(np.uint8)
def randomize_hue(self, image):
"""Varia matiz (cor)"""
hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV).astype(np.float32)
hsv[:, :, 0] = (hsv[:, :, 0] + np.random.uniform(-10, 10)) % 180
return cv2.cvtColor(hsv.astype(np.uint8), cv2.COLOR_HSV2RGB)
def add_noise(self, image):
"""Adiciona ruído Gaussian"""
noise = np.random.randn(*image.shape) * 10
return np.clip(image + noise, 0, 255).astype(np.uint8)
def randomize_blur(self, image):
"""Aplica blur aleatório"""
if np.random.rand() > 0.5:
ksize = np.random.choice([3, 5])
return cv2.GaussianBlur(image, (ksize, ksize), 0)
return image
# Integrar ao ambiente
class RandomizedVisualEnv(VisualPickEnv):
def __init__(self):
super().__init__()
self.randomizer = DomainRandomization()
def render_observation(self):
image = super().render_observation()
# Aplicar randomização durante treino
if self.training:
image = self.randomizer.randomize(image)
return image
7. Integração com ROS 2
Nó ROS 2 para Detecção de Objetos
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Image
from vision_msgs.msg import Detection2DArray, Detection2D, ObjectHypothesisWithPose
from cv_bridge import CvBridge
from ultralytics import YOLO
import cv2
class YOLODetectorNode(Node):
def __init__(self):
super().__init__('yolo_detector')
# Parâmetros
self.declare_parameter('model_path', 'yolov8n.pt')
self.declare_parameter('confidence_threshold', 0.5)
model_path = self.get_parameter('model_path').value
self.conf_thresh = self.get_parameter('confidence_threshold').value
# Carregar modelo
self.model = YOLO(model_path)
self.bridge = CvBridge()
# Subscribers
self.image_sub = self.create_subscription(
Image,
'/camera/image_raw',
self.image_callback,
10
)
# Publishers
self.detection_pub = self.create_publisher(
Detection2DArray,
'/detections',
10
)
self.viz_pub = self.create_publisher(
Image,
'/detections/image',
10
)
self.get_logger().info('YOLO Detector Node started')
def image_callback(self, msg):
# Converter ROS Image para OpenCV
cv_image = self.bridge.imgmsg_to_cv2(msg, desired_encoding='bgr8')
# Inferência
results = self.model(cv_image, verbose=False)
# Criar mensagem de detecções
detections_msg = Detection2DArray()
detections_msg.header = msg.header
for box in results[0].boxes:
if box.conf[0] < self.conf_thresh:
continue
detection = Detection2D()
detection.header = msg.header
# Bbox
x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
detection.bbox.center.x = float((x1 + x2) / 2)
detection.bbox.center.y = float((y1 + y2) / 2)
detection.bbox.size_x = float(x2 - x1)
detection.bbox.size_y = float(y2 - y1)
# Classe
cls = int(box.cls[0])
conf = float(box.conf[0])
hypothesis = ObjectHypothesisWithPose()
hypothesis.id = str(cls)
hypothesis.score = conf
detection.results.append(hypothesis)
detections_msg.detections.append(detection)
# Publicar detecções
self.detection_pub.publish(detections_msg)
# Publicar visualização
annotated = results[0].plot()
viz_msg = self.bridge.cv2_to_imgmsg(annotated, encoding='bgr8')
self.viz_pub.publish(viz_msg)
def main(args=None):
rclpy.init(args=args)
node = YOLODetectorNode()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
💻 Prática Hands-On
Projeto 1: Sistema de Detecção e Tracking
Objetivo: Criar pipeline completo de detecção → tracking → localização 3D.
# visual_tracking_system.py
import cv2
import numpy as np
from ultralytics import YOLO
class VisualTrackingSystem:
def __init__(self, camera_id=0):
self.yolo = YOLO('yolov8n.pt')
self.cap = cv2.VideoCapture(camera_id)
self.tracker = None
self.target_class = None
def run(self):
while True:
ret, frame = self.cap.read()
if not ret:
break
if self.tracker is None:
# Modo detecção
self.detect_and_select(frame)
else:
# Modo tracking
self.track(frame)
cv2.imshow('Visual Tracking', frame)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
elif key == ord('r'): # Reset
self.tracker = None
self.cap.release()
cv2.destroyAllWindows()
def detect_and_select(self, frame):
# Detectar objetos
results = self.yolo(frame)
# Mostrar detecções
annotated = results[0].plot()
cv2.imshow('Visual Tracking', annotated)
# Usuário clica em objeto para trackar
# (implementar seleção por clique)
def track(self, frame):
success, bbox = self.tracker.update(frame)
if success:
x, y, w, h = [int(v) for v in bbox]
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
if __name__ == '__main__':
system = VisualTrackingSystem()
system.run()
Projeto 2: Robô Pega Objeto Usando Visão
Objetivo: Usar YOLO + segmentação para pegar objeto específico.
[Código completo já fornecido nas seções anteriores - combinar detecção, segmentação e grasp planning]
Projeto 3: Treinar Detector Custom
Objetivo: Criar dataset e treinar YOLOv8 para detectar objetos personalizados.
Passo 1: Coletar e anotar imagens
# Usar Roboflow para anotar
# https://roboflow.com/
# Ou usar LabelImg
pip install labelImg
labelImg
Passo 2: Treinar (ver código na seção YOLO)
Passo 3: Avaliar
from ultralytics import YOLO
model = YOLO('runs/detect/train/weights/best.pt')
metrics = model.val()
print(f"mAP50: {metrics.box.map50:.3f}")
print(f"mAP50-95: {metrics.box.map:.3f}")
print(f"Precision: {metrics.box.mp:.3f}")
print(f"Recall: {metrics.box.mr:.3f}")
🏆 Projeto Final
Sistema de Manipulação Visual Completo
Objetivo: Robô detecta, localiza, pega e coloca objetos usando apenas visão.
Requisitos:
- ✅ Detecta 3+ classes de objetos (YOLO custom)
- ✅ Localiza objetos em 3D (RGB-D)
- ✅ Segmenta objeto para grasp preciso
- ✅ Integra com controle de robô (ROS 2)
- ✅ Taxa de sucesso > 80% em 10 tentativas
Deliverables:
- Código completo no GitHub
- Dataset de treinamento (se custom)
- Vídeo de demonstração
- Relatório técnico com métricas
📚 Recursos Adicionais
Papers Importantes
- YOLO: You Only Look Once (Redmon et al., 2016)
- YOLOv8: Ultralytics YOLOv8
- Mask R-CNN: He et al., 2017
- MediaPipe: Google MediaPipe
Bibliotecas Essenciais
pip install opencv-python
pip install ultralytics
pip install mediapipe
pip install detectron2 -f https://dl.fbaipublicfiles.com/detectron2/wheels/cu118/torch2.0/index.html
pip install open3d # Point cloud processing
Datasets
- COCO - 80 classes, 330K imagens
- Open Images - 600 classes, 9M imagens
- Roboflow Universe - Datasets de robótica
✅ Checklist de Conclusão
- Calibrei uma câmera
- Implementei detecção com YOLO
- Treinei um modelo custom
- Realizei segmentação de instâncias
- Estimei poses humanas
- Converti 2D → 3D com precisão
- Integrei visão + RL
- Criei nó ROS 2 de visão
- Completei projeto final
- Publiquei código no GitHub
🚀 Próximos Passos
Você dominou visão computacional para robótica! Próximo módulo:
Módulo 3.3: Large Behavior Models (LBMs)
→ Iniciar Módulo 3.3: Large Behavior Models
Última atualização: 2025-10-29 Autor: Programa FTH Licença: MIT