
13주차 기록을 시작하겠습니다. 🏃🏃🏃🏃
금주에는 Yocto 실습과 한화비전의 카메라를 사용하여 세미나를 진행하였습니다.
한화비전의 기술을 샅샅히 파헤치는 기술 블로그를 작성하고 싶었지만 보안상의 이유로 세미나는 후기 위주로 서술하도록 하겠습니다.
그러면 먼저 Yocto 실습 위주로 정리해보도록 하겠습니다.
2025년 상반기 인턴을 진행하면서 RK3588 보드를 활용해 임베디드 비전 시스템을 개발할 기회가 있었습니다. 당시 adb 쉘을 이용해 접속을 할 때마다 터미널에 뜨는 Yocto라는 문구를 보며 '단순히 OS의 이름인가'보다 라고만 생각했습니다 .
하지만 이번에 Yocto를 배워보고 실습을 진행하며 커스텀 리눅스 배포판을 만들기 위한 강력한 빌드 시스템에 대한 이해를 키울 수 있었습니다 .
기존에는 부트로더부터 앱까지 일일이 수동으로 이식(Porting)했다면, Yocto(Poky)는 이를 레이어 기반의 설정으로 자동화합니다.
Yocto의 작업 순서 (Layer 기반)
"Append(+/-)" 과정이 바로 Yocto의 핵심
1. BSP(Board Support Package) 레이어 추가
- Acme 보드와 같은 특정 하드웨어 구동을 위해
Bootloader, Kernel, Driver 정보가 담긴 레이어(예: meta-acme)를 가져옵니다.
2. Configuration (Distro 설정)
- 어떤 Toolchain을 사용할지, 어떤 라이브러리를 포함할지
local.conf와 bblayers.conf에서 정의합니다.
3. Append & Customize (+/-)
- .bbappend 파일을 통해 기존 레시피를 수정하거나,
App 레시피를 추가하여 필요한 소프트웨어만 골라 담습니다.
4. Build (BitBake)
명령어 한 줄로 Root File System 이미지를 생성합니다.
이때 커널 컴파일부터 파일 시스템 패키징까지 자동으로 진행됩니다.
위의 과정을 거치면 CD/DVD(ISO 이미지) 형태나 SD 카드에 바로 구울 수 있는 WIC 이미지가 생성됩니다.
| 단계 | 수동 포팅 (Manual) | Yocto 자동화 (Automatic) |
|---|---|---|
| 방법 | 소스 수정 및 개별 컴파일 | Recipe(.bb) 작성 및 수정 |
| 관리 | 폴더별 수동 관리 | Layer 단위 모듈화 관리 |
| 도구 | 개별 Toolchain 설치 | SDK 자동 생성 및 포함 |
| 배포 | 압축 파일 및 설치 스크립트 | Final Image (Distro) 형태 출력 |

do_fetch from PREMIRROR ⇒ UPSTREAM → fail ? → MIRROR
시스템 요구 사항
- 최소 90GB 이상의 디스크 여유 공간
- 최소 8GB 의 RAM
# yocto 사용을 위한 여러가지 패키지 설치
$ sudo apt install python3
$ sudo apt install python3-distutils
$ sudo apt install gcc vim xterm net-tools git tree git-core
$ sudo apt install chrpath diffstat g++ gawk make groff
$ sudo apt install libncurses-dev
poky download
git clone -b dunfell git://git.yoctoproject.org/poky.git
→ download 이후 내부 dir enter
환경 변수 설정
$ source oe-init-build-env
빌드 시스템에서 필요로 하는 일련의 shell 환경 설정
→ bblayers.conf 와 local.conf를 만들어 준다.
BBLAYERS : 빌드 환경에 포함된 모든 레이어의 경로 나열
→ 고유 빌드 환경에 레이어를 추가하려면 해당 경로를 여기 추가해야함
모든 빌드 결과물은 tmp 파일 하위에 생성 → 재빌드시 중복을 skip (공유 캐시)
환경 설정 변수
$ bitbake core-image-minimal # 빌드 시작
$ bitbake -c fetchall core-images-minimal # 빌드 하지 않고 소스만 다운
-k : 옵션 : 오류 발생시 작업이 오류와 상관 없으면 계속 수행
애플리케이션 레이어
=====================
사용자 인터페이스 레이어
===========================
배포 레이어
===================================
하드웨어 관련 BSP 레이어
========================================
오픈 임베디드 코어 레이어
=============================================
$ bitbake-layers create-layer meta-helloworld
=> 새 build 폴더 생성
layer 구조 확인
new recipie → new layer
bitbake-layer create-layer [layer name ]
layer 이름은 meta-<??> 이렇게 관용적으로 사용
:~/poky/build-new$ tree meta-helloworld/
meta-helloworld/
├── conf
│ └── layer.conf
├── COPYING.MIT
├── README
└── recipes-example
└── example
└── example_0.1.bb
PN PV PR
3 directories, 4 files
============================================================
$ cat example_0.1.bb
SUMMARY = "bitbake-layers recipe"
DESCRIPTION = "Recipe created by bitbake-layers"
LICENSE = "MIT"
python do_display_banner() { //task 함수 : do_실행 명령()
bb.plain("***********************************************");
bb.plain("* *");
bb.plain("* Example recipe created by bitbake-layers *");
bb.plain("* *");
bb.plain("***********************************************");
}
addtask display_banner before do_build
이처럼 가장 표준적인 형태로 meta-helloworld라는 layer가 만들어짐을 확인할 수 있습니다.
~/poky/build-new/meta-helloworld/recipes-example/example$ cat helloword_01.bb
SUMMARY = "bitbake-layers recipe"
DESCRIPTION = "Recipe created by bitbake-layers"
LICENSE = "MIT"
python do_helloworld() {
bb.plain("***********************************************");
bb.plain("* *");
bb.plain("* helloworld by VEDA *");
bb.plain("* *");
bb.plain("***********************************************");
}
addtask helloworld before do_build
~/poky/build-new$ bitbake-layers add-layer meta-helloworld
NOTE: Starting bitbake server...
작성된 레이어를 bitbake-layers add-layer meta-helloworld 명령어를 통해서 layer에 추가.
-> conf/bblayers.conf에 추가되었는지 확인
ros2man@ros2-100:~/poky/build-new$ cat conf/bblayers.conf
# POKY_BBLAYERS_CONF_VERSION is increased each time build/conf/bblayers.conf
# changes incompatibly
POKY_BBLAYERS_CONF_VERSION = "2"
BBPATH = "${TOPDIR}"
BBFILES ?= ""
BBLAYERS ?= " \ #레이어의 집합 -> 여기다가 추가해야함
/home/ros2man/poky/meta \
/home/ros2man/poky/meta-poky \
/home/ros2man/poky/meta-yocto-bsp \
/home/ros2man/poky/build-new/meta-helloworld \
"
# 1. 특정 레시피의 특정 태스크만 실행
bitbake helloword_01 -c helloworld
==================================Result
***********************************************
* *
* helloworld by VEDA *
* *
***********************************************
여기서 -c 옵션은 이전에 실행되었는지와 관계 없이 특정 task를 실행합니다.
5종류의 파일로 구성
1. 환경설정 파일 (.conf)
2. 레시피 파일(.bb)
3. 클래스 파일 (.bbclass) : 같은 빌드 순서를 공유하는 레시피 상속 제공
4. 첨가 파일 (.bbappend) : 레시피 파일의 확장
5. 인클루드 파일 (.inc): include 와 require 지시자를 통해 다른 파일 include 가능
메타데이터는 두 종류로 나뉨 ( 변수 / 실행 가능한 메타 데이터 )
변수 이름 : 대문자 소문자 숫자 언더 스코어 등등 이 포함 되며 이러한 문자로 시작 가능 -> 하지만 규약에 의해 대문자와 언더스코어만 사용
범위 : conf 파일에서 정의된 변수는 전역 변수 → 모든 레시피에서 보인다
할당
= 직접 할당 → 기호를 통해 변수에 값 할당?= 기본 값 할당 → 이전에 설정 되지 않았다면 기본 값으로 설정= 는 모든 기본 값 할당을 덮어쓴다??= 약한 기본 값 할당 → 파싱 절차가 끝날때 까지 이뤄지지 않음 (가장 마지막 할당이 이뤄짐)확장
즉시 변수 확장
변수 확장은 실제 사용되기 전까지 발생하지 않는다 .
:= 은 할당 시 즉시 확장을 야기

이 때 var4에서 즉시 변수 확장을 야기하면서 var2에서는 var1 = falls on 이 사용됨
변수 후입 및 선입
+= , =+ : 두 값 사이에 공백(공간) 하나를 추가.- , =. : 공백이 붙지 않은 두 값을 추가_append , _prepend 연산자를 이용삭제 : remove → VAR1 _remove = “123” VAR1 변수 안의 값 123 모두 제거
조건부 변수 할당
조건부 후입 선입
인클루드
설정 공유를 통해 다른 메타 데이터 파일을 include 가능
상속
→ 클래스 이름이 빌드 환경에 포함된 모든 메타데이터 레이어를 통틀어 유일해야 한다 .
⇒ 메타 데이터를 변수와 완전히 동일하게 취급
def 키워드를 통해 어떤 파일에서 정의되든지 간에 전역으로 정의 가능 ._annoymous 키워드를 사용해 정의 가능 ⇒ 파싱 절차 끝에서 실행특정 레시피를 위해 비트베이크 명령행에서 직접 호출
빌드 절차의 일부로 비트베이크에 의해 자동 실행
do_download (){
}
addtask download befor do_build
# task 정의 시 순서 까지 설정
task를 실행할지 지정하지 않은 상태에서 호출시 기본 task 실행
1. task dependency
addtask ??? before(after) do_build
2. package dependency
hello
2-1. build dependency (빌드할 때 같이 있어야 되는 것들)
ie) lib : 헤더파일, 정적 라이브러리
2-2. execute dependency (실행할 때 같이 있어야 되는 것들)
ie) .so : 라이브러리 , 환경 설정 파일
빌드시 패키지에 가해진 모든 변경점을 탐지하고 하나 이상의 태스크 결과에 의존하는 소프트웨어 패키지의 태스크도 재실행해야한다.
PROVIDES_prepend = “${PN}” → 할당
1. implicit
hello_1.0.bb
PN(package name) = hello
PV(package version) = 1.0
2. explicit
hello.bb
PN = hello인지 helloworld인지 정확히 알 수 없음
PV = 레시피 안에서 봐야함
3. symbolic
virtual/hello
의존성 선언
DEPENDS = ‘ libxm … . … aaa “ 등등 필요한 라이브러리를 선언
RDEPENDS
다중 제공자
→ 다중 제공자가 있을 때 의존성 선택 : PREFERRED_PROVIDER 사용
버전 선택 : 기본적으로는 최신 버전 선택 / preferred가 있으면 그걸로 택
가장 기본이 되는 이미지인 core-image-minimal을 분석하면 이미지가 어떻게 구성되는지 알 수 있습니다.
meta/recipes-core/images/ 경로에서 기본 이미지 레시피들을 확인할 수 있습니다.
~/poky/meta/recipes-core$ find . | grep core-image-minimal
./images/core-image-minimal-mtdutils.bb
./images/core-image-minimal.bb
./images/core-image-minimal-initramfs.bb
./images/core-image-minimal-dev.bb
core-image-minimal.bb 분석
=====================================================
~/poky/meta/recipes-core/images$ cat core-image-minimal.bb
SUMMARY = "A small image just capable of allowing a device to boot."
IMAGE_INSTALL = "packagegroup-core-boot ${CORE_IMAGE_EXTRA_INSTALL}"
#설치할 패키지 결정
IMAGE_LINGUAS = " "
#언어 설정
LICENSE = "MIT"
inherit core-image # 객체 지향적 개념 => 부모 클래스를 가져와서 물려받음
IMAGE_ROOTFS_SIZE ?= "8192" # 루트파일 시스템의 기본 크기를 8MB로 설정
#?= 설정된 값이 없다면 8192
IMAGE_ROOTFS_EXTRA_SPACE_append = "${@bb.utils.contains("DISTRO_FEATURES", "systemd", " + 4096", "" ,d)}"
# systemd가 포함되어 있으면 4MB 추가
IMAGE_INSTALL: 설치할 패키지 그룹을 결정합니다. (packagegroup-core-boot 등)
inherit core-image: core-image 클래스를 상속받아 기본적인 이미지 생성 기능을 가져옵니다. (객체 지향의 상속 개념)
IMAGE_ROOTFS_SIZE: 루트 파일 시스템의 기본 크기를 설정합니다. (기본 8MB, systemd 사용 시 +4MB)
이미지를 확장하거나 설정을 변경하는 데는 크게 두 가지 방법이 있습니다.
local.conf 수정 (전역 설정)build/conf/local.conf 파일을 수정하여 쉽고 빠르게 설정을 변경할 수 있습니다.
기존 이미지를 상속받아 새로운 .bb 파일을 만드는 것이 정석입니다.
예시: my-custom-image.bb
# 1. 설명
SUMMARY = "My custom Linux image for testing"
# 2. 부모 이미지 상속 (필수)
inherit core-image
# 3. 패키지 추가
IMAGE_INSTALL += " \
packagegroup-core-boot \
${CORE_IMAGE_EXTRA_INSTALL} \
hello \
networkmanager \
"
# 4. 기능 및 파일시스템 설정
IMAGE_FEATURES += "debug-tweaks" # 암호 없는 루트 로그인 등 개발 편의 기능
IMAGE_FSTYPES = "wic.bz2" # 생성할 이미지 타입
사용자 계정 관리 (extrausers)
이미지 레시피 내에서 extrausers 클래스를 상속받아 사용자 및 그룹을 관리할 수 있습니다.
inherit extrausers
EXTRA_USERS_PARAMS = " \
useradd -p '\$6\$rounds=4096\$salt\$hashedpassword' tester; \
usermod -a -G sudo tester; \
passwd-expire tester; \
"
소프트웨어 패키지를 빌드하기 위해서는 레시피 파일(.bb)을 작성해야 합니다.
<pakagename>_<version>-<revision>.bb BPN PV PR
주요 변수
${WORKDIR}/${PN}-${PV}⇒ ie) build/tmp/work/.../hello-2.2/).o 파일(오브젝트 파일)이나 실행 파일이 생성되는 위치do_install 단계에서 실행 파일을 ${D}/usr/bin 같은 곳에 복사해두면, 나중에 Yocto가 이 ${D} 폴더 안의 내용물을 그대로 복사해서 최종 이미지(.wic)를 만든다.

주요 Task (생명주기)
do_fetch: 소스 다운로드
do_unpack: 압축 해제
do_patch: 패치 적용
do_configure: 빌드 환경 설정 (autotools, cmake 등)
do_compile: 소스 컴파일 (${CC} 등 사용)
do_install: 결과물을 ${D} 디렉토리로 복사
→ GNU or CMake가 아니라면 해당 task를 레시피에 생성 해야한다 💡
바이너리 ,라이브러리 , 헤더파일 , 환경 설정파일, 문서파일 등
루트 파일 시스템과 같은 형상을 갖는 파일 시스템을 계층에 복사한다
직접 레이어를 추가하고 C 프로그램을 컴파일하는 레시피를 만들어 보았습니다.
준비된 meta-mylayer를 빌드 디렉토리로 옮기고 등록합니다.
$ mv meta-mylayer ~/yocto/poky/build
$ bitbake-layers add-layer meta-mylayer
이 레시피는 Makefile 없이 직접 컴파일 명령어를 실행하는 방식입니다.
SUMMARY = "bitbake-layers recipe"
LICENSE = "MIT"
# ... (라이선스 체크섬 생략) ...
SRC_URI = "file://hello-1.0.tgz"
S = "${WORKDIR}"
do_compile() {
${CC} -c helloprint.c
${CC} -c hello.c
${CC} -o hello hello.o helloprint.o
}
do_install() {
install -d ${D}${bindir} # /usr/bin 디렉토리 생성
install -m 0755 hello ${D}${bindir} # 실행 파일 복사
}
bitbake -e 명령어를 사용하면 변수가 실제로 어떻게 설정되었는지 확인할 수 있습니다.
설치 경로 확인 (D):
$ bitbake -e hello | grep -w ^D=
D="/home/iam/yocto/poky/build/tmp/work/.../image"
do_install 단계에서 파일이 복사되는 목적지입니다.
작업 경로 확인 (WORKDIR):
$ bitbake -e hello | grep -w ^WORKDIR=
WORKDIR="/home/iam/yocto/poky/build/tmp/work/core2-64-poky-linux/hello/1.0-r0"
빌드가 완료되면 QEMU를 통해 가상 머신에서 테스트합니다.
$ bitbake hello
$ runqemu qemux86-64 nographic
# 로그인 후 hello 명령어 실행 가능
우선 용량이 어마무시하게 많이 필요해서 윈도우와 우분투를 듀얼부팅 해놓은 개발환경에서 용량 정리에 더 많은 시간을 할애하다 많이 놓쳤던 yocto 실습이었습니다. 그래도 bitbake, recipe, layer 등에 대한 기반을 닦았으니 이미지 커스터마이징을 한번쯤 시도해봐야하겠다는 생각이 들었습니다.
과거 RK3588 보드 프로젝트를 진행하며 드라이버 업데이트 등의 이미지 갱신을 요청드렸을 때, 결과물을 받기까지 사흘이라는 시간이 걸렸던 이유를 이제야 깨달았습니다. Yocto 빌드 시스템은 레이어와 레시피 간의 복잡한 의존성을 관리하기 때문에, 작은 핀 설정 변경이라도 시스템 전체의 일관성을 유지하기 위한 재컴파일 및 이미지 패키징 과정이 필수적임을 느끼는 시간이었습니다.
한화 비전의 실제품을 디버깅 해보고 사용해볼 수 있는 기회였습니다.
보안 상 배웠던 내용보다는 후기 위주로 작성해보겠습니다 .
OCR기능을 사용해보기 위해 라이브러리를 개발환경과 현재 기기 내부의 환경과 동일하게 설정하기 위해서 공유해주신 Doc를 활용해 진행했지만 중간 중간 라이브러리 버전문제와 헤더 경로 문제로 인해서 오류가 발생했습니다.
이를 해결하기 위해서 실제 라이브러리 생성 확인과 직접 다운로드 및 삽입을 통해서 에러를 하나씩 해결할 수 있었습니다.
개발환경을 구성하기 위해서 docker 이미지를 배포받아 이를 활용했습니다.
opensdk_packager소스코드를 빌드하고 .cap파일을 통해 웹 client에서 Application 설치를 진행했습니다.
실무의 관점에서 현장에서 디버깅을 할 수 있는 방법부터 최근에 회사에서 연구하고 있는 디버깅 방법까지 설명을 들었습니다. 개발자로서 어떤 환경에서도 문제를 해결하기 위해서 떠올릴 수 있는 방법론에 대해서 알게 되었습니다.
한화 비전의 여러 카메라를 실제로 사용해보고 IP카메라의 특성상 인터넷을 통해 정보를 주고 받고 디버깅을 진행하는 과정에서 많이 배운 시간이었습니다 .
01.13 레전드 가브리살 수육 + 배추 + 무말랭이 + 막국수 + 전병
캠코 양재타워 구내식당 ➡️➡️➡️➡️ 그저 G.O.A.T.
또 신난다가 터져버린 그 녀석 .... 피할 수가 없다 .

자습하며 구현한 조이스틱 + 서보모터 제어
도움 많이 된다. 으휴~ 구현하니까 그냥 대화가 된다 대화가..^^