Sun넘었조 - 차량 운전자 눈부심 방지 선바이저 로봇
| Anti-Glare Sunvisor Robot for Vehicle Driver | ||
|---|---|---|
| Sun넘었조 | ||
| | ||
| 학교 | 서울시립대학교 | |
| 학과 | 기계정보공학과 | |
| 학번 및 성명 | 20204300** | 최*현(팀장) |
| 20194300** | 이*현 | |
| 20204300** | 오*택 | |
| 20204300** | 조*호 | |
| 20204300** | 조*규 | |
목차
프로젝트 개요
프로젝트 요약
파일:프로젝트 시작.jpg 파일:프로젝트 시작.jpg 파일:프로젝트 시작.jpg
프로젝트 명 입력
프로젝트 배경 및 기대효과
프로젝트 개발 목표
경제성
- 재료비
- 불필요한 고사양 부품 사용을 지양하며, 오픈 소스 하드웨어 및 소프트웨어를 적극적으로 활용하고, 각 부품(모터, 구동장치, 제어어장치 등) 선정 시 요구 성능을 만족하면서 전체 시스템 구축 비용을 개발 소요 비용 목표치(500천원) 내에서 해결할 수 있도록 시스템을 구축하였다.
기능성
- 정확성
- 본 시스템은 Glare의 존재 유무에 따라 선바이저의 작동 여부가 결정되고, Glare의 위치에 따라 선바이저가 목표 위치로 이동하는 기능을 갖는다. 따라서 Glare의 존재 유무 및 Glare의 위치를 정확하게 특정할 수 있어야 한다.
- 실시간성
- 본 시스템은 차량 운전 상황에서 동적으로 변화하는 Glare의 위치에 맞게 신속하게 대응할 수 있어야 한다.
안정성
- 구조안정성
- 본 시스템은 차량 주행 중 발생하는 진동 환경에서도 안정적으로 작동할 수 있도록,각 부품의 내구성을 고려하여 설계되어야 한다. 특히 반복적인 선바이저 구동 상황에서도 성능의 저하나 물리적 손상없이 지속적인 작동이 가능하도록 해야한다.
- 유지보수성
- 본 시스템은 제어부와 구동부로 핵심 구성 요소들을 독립적인 모듈 단위로 설계함으로써, 특정 부분에 문제가 발생했을 경우 해당 모듈만 쉽게 분리하여 진단 및 교체가 가능할 수 있도록 하여 유지보수의 편의성을 지니게 하고자 한다.
- 또한, 시스템 전체의 물리적 구조와 배선을 최대한 단순화함으로써, 고장이 발생할 수 있는 잠재적 요소를 줄이며, 문제 발생 시 원인 파악과 해당 부품 접근이 용이하도록 구성하고자 한다.
프로젝트 개요
프로젝트 요약
프로젝트의 배경 및 기대효과
프로젝트 개발 목표
동작 시나리오
본 프로젝트에서 제안하는 시스템은 다음과 같은 4가지 운전 상황에 대해 선바이저 로봇이 각각의 상황에 맞는 동작을 수행하도록 설계되었다.
- Case 1. 전방에 Glare가 존재하지 않는 경우
- 선바이저 로봇은 전방에 Glare가 존재하지 않을 경우
- → 기본 상태인 접힌 형태를 유지
- Case 2. 전방에 Glare가 존재하는 경우
- 선바이저 로봇이 전방의 Glare를 인식하게 될 경우
- → 피에조 부저(Piezo Buzzer)를 통해 운전자에게 경고음 알림을 제공
- → 선바이저가 펼쳐진다.
- → Glare 위치에 해당하는 Grid 좌표로 선바이저 로봇이 이동하여 Glare를 차단
- Case 3. Glare의 위치가 이동하는 경우
- 차량 주행 중 Glare의 위치가 변화하는 경우
- → 선바이저 로봇은 Glare의 움직임을 실시간 추적
- → 현재 위치에 해당하는 Grid 좌표로 계속해서 이동하며 Glare를 차단
- Case 4. 전방의 Glare가 사라진 경우
- 전방에 존재하던 Glare가 사라질 경우
- → 피에조 부저를 통해 운전자에게 알림 제공
- → 선바이저가 접힌 상태로 복귀
구현 내용
하드웨어 설계 및 구현
시스템 개략도
소프트웨어 설계 및 구현
3.2.2) 좌표 변환
이 모듈은 위 Glare Detection 모듈을 통해 2차원 카메라 이미지 평면에서 추출된 Glare의 픽셀 좌표를, 3차원 월드 좌표계에서의 기하학적 분석을 통해 최종적으로 아두이노가 이해할 수 있는 이산적인 3x3 그리드 인덱스로 변환한다. 이 과정의 정확성은 시스템 전체의 Glare 차단 성능과 직결되므로, 정밀한 수학적 모델링과 구현이 요구된다. 성능 최적화 및 시스템 통합을 고려하여 전체 로직은 C++와 OpenCV 라이브러리를 사용하여 작성하였다.
3.2.2.1) OpenCV 내장 함수를 사용한 렌즈 왜곡 보정
카메라 렌즈로 인해 발생하는 이미지의 방사 왜곡(radial distortion) 및 접선 왜곡(tangential distortion)은 픽셀 위치의 정확도를 저해하는 주요 요인이다. 특히, 이미지 중심부에서 멀어질수록 왜곡의 정도가 심해져, 3D 공간으로의 역투영 시 큰 오차를 유발한다. 따라서, 입력된 Glare의 중심 픽셀 좌표는 가장 먼저 렌즈 왜곡 보정 과정을 거친다.
undistortPoints
- 이 보정 과정은 OpenCV 라이브러리에서 제공하는 cv::undistortPoints 함수를 통해 수행된다. 이 함수는 사전에 카메라 캘리브레이션(Camera Calibration)을 통해 획득한 카메라 고유의 내부 파라미터 행렬(Intrinsic Matrix, K)과 렌즈 왜곡 계수(Distortion Coefficients, D)를 사용한다. - 카메라 내부 파라미터 행렬 (K) <math> K = \begin{bmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{bmatrix} </math>
- fx, fy : 렌즈의 초점 거리를 픽셀 단위로 표현한 값.
- cx, cy : 이미지의 주점(Principal Point), 즉 렌즈의 광학축이 이미지 센서와 만나는 점의 픽셀 좌표.
- 왜곡 계수 (D)
- D = [k1, k2, p1, p2, k3, …]
- k1, k2, k3 등은 방사 왜곡을, p1, p2는 접선 왜곡을 모델링하는 계수이다.
- cv::undistortPoints 함수는 입력된 왜곡된 2D 포인트를 이 파라미터들을 이용하여, 왜곡이 제거된 정규화된 카메라 좌표계(normalized camera coordinates)의 포인트로 변환한다. 이 정규화된 좌표는 카메라 중심을 원점으로 하고 초점 거리를 1로 가정한 평면에서의 좌표이다. - 이후 함수에 카메라 행렬을 인자로 전달함으로써, 정규화된 좌표를 다시 우리가 사용하는 픽셀 좌표계로 재투영하여 정확한 Glare 중심 좌표를 얻는다. 이 과정은 다음과 같은 C++ 코드로 구현되었다.
cv::Mat sun_center_mat(1, 1, CV_64FC2);
sun_center_mat.atcv::Vec2d(0, 0)[0] = sun_center.first;
sun_center_mat.atcv::Vec2d(0, 0)[1] = sun_center.second;
cv::Mat undistorted_points_mat;
cv::undistortPoints(
sun_center_mat, // 입력: 왜곡된 픽셀 좌표
undistorted_points_mat, // 출력: 보정된 좌표를 담을 Mat
DEFAULT_CAMERA_MATRIX, // 카메라 내부 파라미터 행렬 (K)
DEFAULT_DISTORTION_COEFFICIENTS, // 렌즈 왜곡 계수 (D)
cv::noArray(), // R: Optional rectification transform
DEFAULT_CAMERA_MATRIX // P: New camera intrinsic matrix (K와 동일하게 설정)
);
// undistorted_points_mat에는 이제 왜곡이 보정된 픽셀 좌표가 저장됨
cv::Vec2d corrected_sun_vec = undistorted_points_mat.atcv::Vec2d(0, 0);
std::pair<double, double> corrected_sun_center = {corrected_sun_vec[0], corrected_sun_vec[1]};
3.2.2.2) Ray-plane intersection을 이용하여 좌표 변환
왜곡 보정된 픽셀 좌표를 최종적으로 그리드 인덱스로 변환하기 위해, 3D 기하학에 기반한 Ray-plane intersection 기법을 사용한다. 이 과정은 카메라 좌표계의 방향 벡터를 정의하고, 이를 운전자 시점의 월드 좌표계로 변환하여 3차원 교차점을 계산하는 로직으로 구현되었다.
- 1. 전면 유리 평면과의 교차점 계산
- 픽셀 좌표 정규화 : 왜곡 보정된 픽셀 좌표를 이미지의 주점(cx, cy)을 원점으로 하고, 경계를 [-1, 1] 범위로 갖는 정규화된 이미지 평면 좌표 (norm_x, norm_y)로 변환한다. 이는 후속 계산을 이미지 해상도에 독립적으로 만들기 위함이다.
- 3D 방향 벡터 계산 : 이 정규화된 좌표와 카메라의 수평/수직 화각(FOV) 정보를 삼각함수와 결합하여, 카메라 좌표계 기준의 3차원 방향 벡터 D_vec(du, dv, dw)를 계산한다. 이 벡터는 카메라 렌즈의 중심에서 Glare를 향하는 “카메라의 시선”을 나타낸다. 이때, 카메라가 물리적으로 x축 기준으로 일정 각도만큼 위를 보도록 설치된 점을 반영하기 위해, 계산된 기본 방향 벡터에 x축 회전 행렬을 곱하여 최종 방향 벡터를 얻는다. 이는 카메라의 물리적 방향(orientation)을 수학적으로 보정하는 과정이다.
double norm_x = (x_center_px / img_w - 0.5) * 2.0;
double norm_y = (y_center_px / img_h - 0.5) * 2.0;
double angle_x_rad = to_radians(norm_x * (fov_x_deg / 2.0));
double angle_y_rad = to_radians(norm_y * (fov_y_deg / 2.0));
// 회전 전 기본 방향 벡터 계산 (카메라 전방 +Z 가정)
cv::Vec3d D_no_rotation(std::tan(angle_x_rad), -std::tan(angle_y_rad), 1.0);
// 카메라 Pitch 회전 적용
double pitch_rad = to_radians(DEFAULT_CAMERA_PITCH_DEGREES);
cv::Matx33d rotation_matrix_x(1, 0, 0,
0, std::cos(pitch_rad), -std::sin(pitch_rad),
0, std::sin(pitch_rad), std::cos(pitch_rad));
cv::Vec3d D_rotated = rotation_matrix_x * D_no_rotation;
cv::Vec3d D_vec = cv::normalize(D_rotated); // 최종 방향 벡터
- Ray-plane intersection : “태양은 매우 멀리 있어 카메라와 운전자의 시선이 평행하다”는 핵심 가정을 사용하여, 위에서 계산된 D_vec을 운전자 시점의 방향 벡터로 간주한다. 운전자의 눈 위치(DEFAULT_DRIVER_POS)를 광선의 시작점(Origin), D_vec을 방향(Direction)으로 하는 3차원 광선을 정의한다. 이 광선이 차량 전면 유리(월드 좌표계에서 z = DEFAULT_WINDSHIELD_Z로 정의된 평면)와 교차하는 3차원 물리적 지점 P_ws(px, py, pz)를 계산한다.
cv::Point3d intersection = ray_plane_intersection(driver_eye_pos, D_vec, windshield_z);
- 2. 그리드 인덱스 좌표 변환
- 계산된 전면 유리와의 교차점 P_ws의 월드 x, y 좌표를, 선바이저가 실제로 작동하게 될 물리적 영역(DEFAULT_GLASS_ORIGIN, DEFAULT_GLASS_SIZE)을 기준으로 3x3 그리드의 최종 인덱스 (grid_x, grid_y)로 변환한다. 이 과정은 교차점의 상대적인 위치를 비례식으로 계산하여 수행되며, 계산된 값이 그리드 범위를 벗어나는 경우 경계값으로 강제 조정하는 로직을 포함한다.
int grid_x = static_cast<int>(((px - x_left_glass) / glass_width) * grid_cols);
int grid_y = static_cast<int>(((y_top_glass - py) / glass_height) * grid_rows);
grid_x = std::max(0, std::min(grid_x, grid_cols - 1));
grid_y = std::max(0, std::min(grid_y, grid_rows - 1));
3.2.2.3) 메인 제어 루프에서의 모듈 호출
메인 제어 루프에서는 Glare가 감지되었을 때, glare_is_detected_flag 플래그를 활성화한다. 이 플래그가 true 일 때만, 감지된 Glare의 중심 픽셀 좌표 (avg_glarePos)를 camera_to_driver_coords 함수에 전달하여 최종 그리드 좌표를 계산하도록 구현되었다. 이는 불필요한 연산을 줄이고 시스템 효율을 높인다.
bool glare_is_detected_flag = (avg_glarePos.x != -1 && avg_glarePos.y != -1);
std::pair<int, int> grid_coords = {-1, -1};
if (glare_is_detected_flag) {
std::pair<double, double> sun_center_for_transform = {
static_cast<double>(avg_glarePos.x),
static_cast<double>(avg_glarePos.y)};
grid_coords = camera_to_driver_coords(sun_center_for_transform);
}
3.2.3) RPi-Arduino 통신
본 시스템은 효율적인 자원 활용과 실시간성 확보를 위해 분산 제어 아키텍처를 채택하였다. 연산 집약적인 고수준 제어(Glare 인식, 좌표 변환)는 라즈베리파이(RPi)가 담당하며, 실시간성과 정밀성이 요구되는 저수준 하드웨어 제어(모터 구동)는 아두이노 메가(Arduino)가 담당한다. 이 두 이종 프로세서 간의 원활한 정보 교환을 위해, UART 기반의 유선 시리얼 통신 모듈을 C++로 구현하였다.
3.2.3.1) 시리얼 통신을 위한 파일 디스크립터 열기
Linux 기반의 RPi에서 시리얼 포트(/dev/ttyACM0 등)와 통신하기 위해서는, 먼저 해당 장치 파일을 열어 파일 디스크립터(File Descriptor)를 얻어야 한다. 이 과정은 통신을 위한 초기화 단계의 핵심이다.
- 1. initialize
- 아두이노와의 시리얼 통신 시작을 위해, 지정된 시리얼 포트 장치 파일을 열고 통신 설정을 구성하는 함수이다.
- 2. open (시리얼 포트 열기)
- POSIX 표준 함수인 open()을 사용하여 지정된 시리얼 포트 장치 파일(예: /dev/ttyACM0)을 읽고 쓸 수 있는 모드(0_RDWR)로 연다.
- 0_NOCTTY : 이 포트가 프로그램을 실행하는 프로세스의 제어 터미널이 되지 않도록 설정한다. 이는 시리얼 통신 프로그래밍에서 예상치 못한 터미널 제어 신호의 간섭을 막기 위한 일반적인 설정이다.
- 3. 논블로킹 모드 설정
- fcntl() 함수를 사용하여 열린 파일 디스크립터의 속성을 변경한다. 라즈베리파이의 메인 루프가 아두이노의 상태와 관계없이 항상 신속하게 동작하도록 하기 위해, 시리얼 포트를 논블로킹(Non-blocking) 모드로 설정합니다.
- fcntl(…, 0)을 통해 현재 파일 상태 플래그를 읽어옵니다.
- 읽어온 플래그에 0_NONBLOCK 플래그를 추가(OR 연산)합니다.
- fcntl(…, flags)를 통해 수정된 플래그를 포트에 다시 설정합니다.
- 이렇게 논블로킹 모드로 설정하면, write() 함수 호출 시 아두이노 측의 수신 준비 여부나 커널 출력 버퍼 상태와 관계없이 즉시 반환됩니다. 만약 버퍼가 가득 차서 데이터를 보낼 수 없다면, write()는 대기하지 않고 오류(EAGAIN 또는 EWOULDBLOCK)를 반환합니다. 이는 RPi 프로그램 전체가 시리얼 쓰기 작업 때문에 멈추는 현상을 방지하여 시스템의 전체적인 실시간성을 보장하는 핵심적인 설정입니다.
bool initialize(const std::string &port_name, speed_t baud_rate) {
serial_port_fd = open(port_name.c_str(), O_RDWR | O_NOCTTY);
if (serial_port_fd < 0) { /* 오류 처리 */ return false; }
// Non-blocking 설정
int flags = fcntl(serial_port_fd, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(serial_port_fd, F_SETFL, flags);
// ...
}
3.2.3.2) 시리얼 모드 설정
1바이트 크기의 패킷으로 구성된 바이너리 데이터를 아두이노에 정확히 전달하기 위해, 운영체제 수준의 문자 처리를 비활성화하는 Raw 모드로 시리얼 통신 모드를 설정한다.
- 1. tcgetattr로 구조체 선언
- 현재 시리얼 포트의 터미널 속성(Baud rate, 데이터 비트, 패리티 등)을 읽어와 termios 구조체 tty에 저장한다. 이후 이 구조체의 값을 수정하여 새로운 설정을 적용한다.
- 2. 1바이트 패킷 데이터 통신을 위한 모드 설정
- cfmakeraw(&tty) : 이 함수는 tty 구조체를 “raw” 모드로 설정한다. Raw 모드는 운영체제 수준의 입력/출력 문자 처리(예: 에코, 줄바꿈 준자 변환, 특수 문자 해석 등)를 대부분 비활성화하여, 프로그램이 순수한 바이너리 데이터를 주고받을 수 있도록 한다. 1바이트 명령을 정확히 전송하기 위해 필수적이다.
- tty.c_cflag : 제어 플래그를 설정한다. 8 데이터 비트, 패리티 없음, 1 스톱 비트 (8N1) 구성을 명시하고, 하드웨어 흐름 제어(CRTSCTS)는 활성화하여 통신의 안정성을 높였다. CLOCAL과 CREAD를 통해 모뎀 제어 신호를 무시하고 수신을 활성화한다.
- tty.c_lflag, tty.c_iflag, tty.c_oflag : Canonical 모드, 에코, 인터럽트 신호 처리, 소프트웨어 흐름 제어, 출력 후처리 등을 모두 비활성화하여 Raw 데이터 통신을 보장한다.
- tty.c_cc[VMIN], tty.c_cc[VTIME] : read() 함수의 타임아웃 동작을 제어한다. (현재 쓰기 동작이 중심이므로 기본값 유지)
struct termios tty;
if (tcgetattr(serial_port_fd, &tty) != 0) { /* 오류 처리 */ }
cfmakeraw(&tty);
tty.c_cflag |= (CLOCAL | CREAD | CRTSCTS); // 하드웨어 흐름 제어 활성화
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8;
// ... 기타 플래그 설정 ...
- 3. Baud rate, tcflush, tcsetattr
- cfsetispeed, cfsetospeed 함수로 입출력 Baud Rate를 115200으로 설정한다.
- tcflush 함수로 포트를 열 때 수신 버퍼에 남아있을 수 있는 이전 데이터들을 모두 버린다.
- tcsetattr 함수로 수정된 tty 구조체의 설정을 시리얼 포트에 즉시 적용한다. 이 함수가 성공적으로 호출되어야 모든 설정이 반영된다.
3.2.3.3) Arduino로 데이터 전송
위에서 초기화된 시리얼 포트를 통해 Arduino로 데이터를 전송하도록 하는 함수 sendCommandToArduino를 구현하였다. 이 함수는 고수준의 제어 정보(Glare 유무, 그리드 좌표)를 1바이트 통신 프로토콜에 맞춰 패킹하고 전송하는 역할을 한다.
- 1. 전송 데이터 구조 (1바이트 command_byte)
- 최소한의 데이터로 필요한 제어 정보를 전달하기 위해, 총 5비트의 핵심 정보를 하나의 바이트 데이터로 패킹하여 전송한다.
| Bit Position | Bit Index | Field Name | Bits | Value | Description |
|---|---|---|---|---|---|
| MSB | 7 | G (Glare Flag) | 1 | 0 or 1 | Glare 감지 유무를 나타내는 플래그 (1 : 감지) |
| 6 | Reserved | 1 | 0 | 예약 비트 (향후 확장용) | |
| 5 | Reserved | 1 | 0 | 예약 비트 (향후 확장용) | |
| 4 | Reserved | 1 | 0 | 예약 비트 (향후 확장용) | |
| 3 | C1 (Column MSB) | 1 | 0 or 1 | 3x3 그리드의 컬럼(x) 좌표 최상위 비트 | |
| 2 | C0 (Column LSB) | 1 | 0 or 1 | 3x3 그리드의 컬럼(x) 좌표 최하위 비트 | |
| 1 | R1 (Row MSB) | 1 | 0 or 1 | 3x3 그리드의 로우(y) 좌표 최상위 비트 | |
| LSB | 0 | R0 (Row LSB) | 1 | 0 or 1 | 3x3 그리드의 로우(y) 좌표 최하위 비트 |
- 2. sendCommandToArduino 함수 핵심 로직
- unsigned char command_byte를 0으로 초기화한다. 이는 Glare가 감지되지 않았거나, 감지되었더라도 유효한 위치를 찾지 못했을 경우 기본적으로 “선바이저 접기” 명령을 보내기 위함이다.
- 입력받은 glare_detected가 true이고, grid_coords가 유효한 좌표일 때만 명령 생성을 시작한다.
- 명령 바이트 생성
- command_byte |= ( 1 << 7 ) : 최상위 비트를 1로 설정하여 Glare가 감지되었음을 표시한다.
- grid_coords의 col 좌표 (0, 1, 2)를 2비트로 변환하고, << 2 비트 시프트 연산을 통해 명령 바이트의 비트 3과 2에 위치시킨다.
- grid_coords의 row 과표 (0, 1, 2)를 2비트로 변환하고, 명령 바이트의 비트 1과 0에 위치시킨다.
- 디버깅용 콘솔 출력 : 최종적으로 생성된 command_byte의 값을 이진수와 십진수로 콘솔에 출력하여, RPi가 의도한 대로 명령을 생성했는지 개발자가 확인할 수 있도록 한다.
- Arduino로 명령 바이트 전송 : 구성된 1바이트 명령을 실제로 아두이노로 전송하는 내부 함수 sendByte를 호출한다. sendByte 함수는 write() 시스템 콜을 사용하며, 논블로킹 모드로 설정되어 RPi의 메인 루프가 불필요하게 대기하는 것을 방지한다.
bool sendCommandToArduino(bool glare_detected, const std::pair<int, int>& grid_coords) {
unsigned char command_byte = 0;
if (glare_detected) {
if (grid_coords.first != -1 && grid_coords.second != -1) {
command_byte |= (1 << 7);
// ... (col, row 유효성 검사) ...
unsigned char col_bits = static_cast<unsigned char>(grid_coords.first) & 0x03;
unsigned char row_bits = static_cast<unsigned char>(grid_coords.second) & 0x03;
command_byte |= (col_bits << 2);
command_byte |= row_bits;
}
}
return sendByte(command_byte);
}
프로젝트 결과
최종 결과물
결과물 사진 혹은 시연 영상 등
미구현 내용
사진 올리기 테스트
프로젝트 평가
평가 항목 및 결과
추가 평가 사항
느낀점
부록
위키페이지 작성을 위한 문법 가이드
- 표
표는 위키 문법에 맞추어 작성할 수 있습니다. Mediawiki table generator를 이용하면 손쉽게 표를 작성하여 위키 문법으로 export할 수 있습니다. 아래는 Mediawiki table generator를 이용하여 작성한 표의 예시입니다. 위 웹페이지에서는 직접 CSV파일을 가져와서 바로 표를 만들 수도 있습니다. 직접 표를 문법에 맞추어 편집하고자 하시는 분들은 wiki 표 문법을 참조하면 도움이 됩니다.
| 구분 | 실험 1 | 실험 2 | 실험 3 | 실험 4 |
|---|---|---|---|---|
| 결과 1 | 1.1 | 2.1 | 3.1 | 4.1 |
| 결과 2 | 1.2 | 2.3 | 4.5 | 6.4 |
| 결과 3 | 5.1 | 5.4 | 2.7 | 8.5 |
- 수식
원래 위키백과에서는 math 태그를 이용하여 바로 수식을 작성할 수 있지만 capstone wiki에서 그 기능은 지원되지 않는것으로 확인됩니다. 따라서 수식을 올리기 위해서는 수식을 사진으로 변환한 후 올려야 합니다. LATEX 수식 생성기 를 이용하면 tex 문법을 이용하여 수식을 작성하여 파일로 저장할 수 있습니다.
위 수식은 support vector machine의 비용 함수를 표현한 예시입니다. tex 문법은 tex 수식 문법 에서 확인할 수 있습니다.
- 사진
사진은 "도구-파일 올리기" 탭에서 파일을 올린 후 아래와 같이 올릴 수 있습니다. 파일명은 파일 올리기에서 정한 "파일의 새 이름"을 사용하면 됩니다.
시립대 엠블렘 예시
- 코드
코드는 syntaxhighlight 기능을 이용하여 아래와 같이 표현할 수 있습니다.
#include <iostream>
int main ( int argc, char **argv ) {
std::cout << "Hello World!";
return 0;
}
이에 대한 자세한 내용은 Mediawiki syntaxhighlight를 참고하면 도움이 됩니다.



