embeded/robot2026. 6. 30. 15:18

위대하신(!) GPT 님께 질문을 바꾸어서 해보니 답을 내려주시었다!

from pymycobot.mycobot import MyCobot

mc = MyCobot("/dev/ttyAMA0", 1000000)

# Tool TCP 설정
# x, y, z, rx, ry, rz
mc.set_tool_reference([0, 0, 100, 0, 0, 0])

# End Type을 Tool로 변경
mc.set_end_type(1)

[링크 : https://chatgpt.com/share/6a435d66-1bd0-83ee-a338-b2b1932bee98]

 

send_angles([0,0,0,0,0,0], 10) 을 이용해서 쭉 펴면

이렇게 생겨 먹어서 굳이... 하자면 아래 기준 y축 방향이긴 한데

[링크 : https://docs.elephantrobotics.com/docs/gitbook-en/2-serialproduct/2.1-280/2.1.5-PI-2023.html]

 

gpt로 물어보고 다시 설명을 보니 다 써있었구나.. -_ㅠ

T in the figure is the set tool coordinate system. The posture of this coordinate system is consistent with O’, and the relative displacement of the origin has occurred. Use the python function to set the tool coordinate system:

set_tool_reference([x, y, z, rx, ry, rz]) //Set tool coordinate system
set_end_type(1) //Set the end coordinate system type as tool
Assume that the tool coordinate system T is not rotated relative to O' (rx = ry = rz = 0)
Assume that the origin of the tool coordinate system T is in the coordinate system O’ at (x = 0, y = 0, z = 100mm)
The final tool coordinate system parameter is set_tool_reference(0, 0, 100, 0, 0, 0)

 

그런데 왜 끝부분의 z 축은 가장 아래의 x 축을 보고 있는걸까?

 

[링크 : https://docs.elephantrobotics.com/docs/gitbook-en/2-serialproduct/2.1-280/Kinematics&Coordinate.html]

 

2.3 set_tool_reference(coords)

Function: Set Tool coordinate system。
Parameters:
coords: The coordinate value of [x, y, z, rx, ry, rz] has a length of 6, x, y, z ranging from - 280 to 280, and rx, ry, yz ranging from - 314 to 314
Return Value: None


2.4 get_tool_reference()

Function: Get Tool coordinate system。
Return Value: Returns a coordinate list with a length of 6


2.9 set_end_type(end)

Function: Set end coordinate system。
Parameters:
end: 0 - flange(default),1 - tool
Return Value: None


3.0 get_end_type()

Function: Get end coordinate system
Return Value: 0 - flange(default),1 - tool, -1 - error

[링크 : https://docs.elephantrobotics.com/docs/gitbook-en/7-ApplicationBasePython/7.3_coord.html]

 

+

해보니 잘 된다.

set_end_type() 의 문제였던 듯.

set_end_type(1) 해주고 나서 set_coords() 에서 rx 를 오가게 하니 된다.

'embeded > robot' 카테고리의 다른 글

mycobot 280 pi / python  (0) 2026.06.29
왼손 오른손 좌표계  (0) 2026.06.22
mycobot280_pi urdf  (0) 2026.06.22
블루로봇 밸런스 스테이지  (0) 2026.06.21
elephant robotics cobot python api  (0) 2026.06.19
Posted by 구차니
Programming/qt2026. 6. 30. 12:40

connect() 함수를 사용하면 대개 인자를 4개만 넣고 쓰는데

함수 프로토타입을 보니 가장 마지막 인자가 기본값으로 지정되어 있다.

// qobject.h
    static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
                                     const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot,
                                     Qt::ConnectionType type = Qt::AutoConnection)

 

기본은 Qt:AutoConnection 이라는데 약간의 손실 가능성이 존재하고

반드시 손실되서는 안되는 경우라면 QueuedConnection으로 하는것을 추천한다고 한다.

// qnamespace.h
    enum ConnectionType {
        AutoConnection,
        DirectConnection,
        QueuedConnection,
        BlockingQueuedConnection,
        UniqueConnection =  0x80
    };

 

DirectConnection은 다른 스레드에서 쓰면 동기화 문제 발생할수 있다는걸 보면 동일 쓰레드 내에서 써야 할듯.

[링크 : https://still.tistory.com/86]

 

 

[링크 : https://doc.qt.io/qt-6/qt.html]

'Programming > qt' 카테고리의 다른 글

qt5 qml 다국어지원  (0) 2026.06.30
qt5 qml Q_PROPERTY  (0) 2026.06.30
qt5 qml connections  (0) 2026.06.30
qt 동적 해상도 대응  (0) 2026.06.29
QT 다국어 지원, qm 만 교체 할수 있도록 변경  (0) 2026.06.29
Posted by 구차니
Programming/qt2026. 6. 30. 12:31

언제나 그렇듯(?) tr() 대신 qsTr()로 감싸줘야 한다.

Text {
    id: txt1;
    text: qsTr("Back");
}

[링크 : https://doc.qt.io/archives/qt-5.15/qtquick-internationalization.html]

 

qt5.10 이전에는 retranslate()가 정상적으로 작동하지 않는 문제가 있었다고.

함수 레벨이 다름에 주의.

그런데.. installTranslator 하기 전에 removeTranslator 해줘야 하나?

    if(lang == "bg") {
     qApplication->removeTranslator(&trEN);
     qApplication->installTranslator(&trBG);
    } else if (lang == "en") {
     qApplication->removeTranslator(&trBG);
     qApplication->installTranslator(&trEN);
    }

    qmlEngine->retranslate();

[링크 : https://forum.qt.io/topic/90018/problem-with-dynamic-language-change-in-qml]

[링크 : https://wiki.qt.io/How_to_do_dynamic_translation_in_QML]

'Programming > qt' 카테고리의 다른 글

qt signal / slot, connect() Qt::AutoConnection  (0) 2026.06.30
qt5 qml Q_PROPERTY  (0) 2026.06.30
qt5 qml connections  (0) 2026.06.30
qt 동적 해상도 대응  (0) 2026.06.29
QT 다국어 지원, qm 만 교체 할수 있도록 변경  (0) 2026.06.29
Posted by 구차니
Programming/qt2026. 6. 30. 12:24

uml 외부와 내부를 연결해주려면 Q_PROPERTY를 이용해서 변수를 지정하고

해당 변수를 건드릴 함수를 연동해주면 된다.

class UIController : public QObject
{
    Q_OBJECT

    // ── 페이지 ──────────────────────────────────────────────
    Q_PROPERTY(int currentPage
               READ  currentPage
               WRITE setCurrentPage
               NOTIFY currentPageChanged)

    // ── 센서 / 데이터 값 ────────────────────────────────────
    Q_PROPERTY(double temperature
               READ  temperature
               NOTIFY temperatureChanged)

    Q_PROPERTY(double progress
               READ  progress
               NOTIFY progressChanged)

    // ── 텍스트 ──────────────────────────────────────────────
    Q_PROPERTY(QString statusText
               READ  statusText
               NOTIFY statusTextChanged)

[링크 : https://doc.qt.io/qt-6/qtqml-cppintegration-exposecppattributes.html]

 

다만 Q_PROPERTY로 연결이 가능한 타입에는 제한이 있다.

개인이 만든 구조체 등은 못 넘기는 듯?

[링크 : https://doc.qt.io/qt-6/qtqml-cppintegration-data.html]

'Programming > qt' 카테고리의 다른 글

qt signal / slot, connect() Qt::AutoConnection  (0) 2026.06.30
qt5 qml 다국어지원  (0) 2026.06.30
qt5 qml connections  (0) 2026.06.30
qt 동적 해상도 대응  (0) 2026.06.29
QT 다국어 지원, qm 만 교체 할수 있도록 변경  (0) 2026.06.29
Posted by 구차니
Programming/qt2026. 6. 30. 11:39

claude에게 qt5 qml 예제를 만들어 달라고 하고  코드 분석중

이전에는 못봤던 Connections 라는 키워드가 눈에 띈다

// Main.qml
ApplicationWindow {
    id: root

    // uiController.currentPage 변화 → StackLayout 전환
    Connections {
        target: uiController
        // Qt5(Qt5.4+)는 함수형 핸들러 onXxxChanged 동일하게 지원
        onCurrentPageChanged: {
            stack.currentIndex = uiController.currentPage
        }
    }

    ColumnLayout {
        anchors.fill: parent
        spacing: 0
        // ── 페이지 스택 (StackedWidget 대응) ──────────────
        StackLayout {
            id: stack
            Layout.fillWidth: true
            Layout.fillHeight: true
            currentIndex: 0

            Loader { source: "HomePage.qml" }
            Loader { source: "DataPage.qml" }
            Loader { source: "SettingsPage.qml" }
        }

 

engine 에다가 setContextProperty 하는건 engine.load() 이후에 해도 적용되긴 한다.

// main.cpp
    // ── 인스턴스 생성 ─────────────────────────────────────
    SocketClient  socketClient;
    UIController  uiController;

    // ── 소켓 → UI 컨트롤러 시그널 연결 ───────────────────
    QObject::connect(&socketClient, &SocketClient::pageChangeRequested,
                     &uiController,  &UIController::onPageChange);

    QObject::connect(&socketClient, &SocketClient::valueChangeRequested,
                     &uiController,  &UIController::onValueChange);

    QObject::connect(&socketClient, &SocketClient::textChangeRequested,
                     &uiController,  &UIController::onTextChange);

    // ── QML 엔진 설정 ─────────────────────────────────────
    QQmlApplicationEngine engine;

    // QML에서 'socketClient', 'uiController' 이름으로 접근 가능
    engine.rootContext()->setContextProperty("socketClient", &socketClient);
    engine.rootContext()->setContextProperty("uiController", &uiController);

 

// socketclient.h
class SocketClient : public QObject
{
    Q_OBJECT
    Q_PROPERTY(bool connected READ isConnected NOTIFY connectedChanged)
    Q_PROPERTY(QString clientAddress READ clientAddress NOTIFY clientAddressChanged)

public:
    explicit SocketClient(QObject *parent = nullptr);
    ~SocketClient();

    bool isConnected() const { return m_socket != nullptr; }
    QString clientAddress() const { return m_clientAddress; }

    Q_INVOKABLE void startListening(int port = 9000);
    Q_INVOKABLE void stopListening();

signals:
    void connectedChanged();
    void clientAddressChanged();

    // 파싱된 명령 시그널
    void pageChangeRequested(int index);

 

// uicontroller.cpp
void UIController::setCurrentPage(int page)
{
    if (m_currentPage == page) return;
    m_currentPage = page;
    emit currentPageChanged();
}

void UIController::onPageChange(int index)
{
    setCurrentPage(index);
}

 

[링크 : https://swjs.tistory.com/entry/QTQML-QML의-Connections]

[링크 : https://doc.qt.io/qt-6/ko/qml-qtqml-connections.html]

'Programming > qt' 카테고리의 다른 글

qt5 qml 다국어지원  (0) 2026.06.30
qt5 qml Q_PROPERTY  (0) 2026.06.30
qt 동적 해상도 대응  (0) 2026.06.29
QT 다국어 지원, qm 만 교체 할수 있도록 변경  (0) 2026.06.29
QGraphicsProxyWidget  (0) 2026.06.05
Posted by 구차니
개소리 왈왈/컴퓨터2026. 6. 30. 10:23

일요일 부터 버려진 컴퓨터가 h81 이길래 그냥 주울 생각을 안하다가

오늘 혹시나 해서 cpu만 적출해볼까 하면서 열어보니 오오~!

 

다행히 잘 켜진다 ㅋㅋ

써멀 없이 그냥 쿨러만 달아서 온도가 어우

'개소리 왈왈 > 컴퓨터' 카테고리의 다른 글

포맷할때가 되었나..  (0) 2026.06.19
iptime as 접수  (0) 2026.06.15
asrock B360M pro4  (0) 2026.05.01
지름 - 케이스, 램  (0) 2026.04.30
되는게 없네 ㅋㅋㅋ - 라이트 컴 NA210  (0) 2026.04.30
Posted by 구차니

우회전 하는데 맞은편 좌회전이 너무 내쪽으로 붙어서

그걸 피한다고 이르게 회전했더니 뒷 타이어 연석에 긁어 먹었다.

제법 충격이 커서 tpms 바로 확인ㅇ하는데 공기압은 문제가 없어서 일단은 운행했는데

내리고 나서 확인해보니 제법 크고 깊게 찢어졌다.

 

그냥 보기에는 살짝이네~ 싶지만

 

들어서 보면 제법 깊게 파였다 ㅠㅠ

'개소리 왈왈 > 육아관련 주저리' 카테고리의 다른 글

베란다 문 잠금장치 수리  (2) 2026.06.23
바쁜주말  (2) 2026.05.31
밀크티 타블렛(s6 lite) 초기화 하기  (0) 2026.05.23
고향  (0) 2026.05.03
유류비 지원 때문인가?  (0) 2026.05.01
Posted by 구차니
embeded/robot2026. 6. 29. 18:09

atom 이랑 팔에 힘빼는거(!) 테스트

 

from pymycobot.mycobot import MyCobot
from pymycobot import PI_PORT, PI_BAUD
mc = MyCobot(PI_PORT, PI_BAUD)
mc.set_color(0,0,255)
mc.get_digital_input(39) # atom 누르지 않고
1
mc.get_digital_input(39) # atom 누르고
0
mc.power_off() # atom off
mc.get_digital_input(39) # atom 안 읽힘
mc.get_digital_input(39)
mc.power_on() # atom on
mc.get_digital_input(39) # atom 누르지 않고
1
mc.get_digital_input(39) # atom 누르고
0
mc.is_power_on()
1
mc.power_off()
mc.is_power_on()
0
mc.power_on()


mc.power_off()
mc.is_all_servo_enable()
mc.power_on()
mc.is_all_servo_enable()

 

power_on 은 atom(LED, 버튼 일체형) 통신 뿐만 아니라

servo 전원 on/off도 같이 엮인다.

1.1 power_on()     Function: Atom open communication (default open)
1.2 power_off()     Function: Atom turn off communication
1.3 is_power_on() Function: judge whether robot arms is powered on or not

 

서보(관절별) 잠그고, 푸는 명령

2.11 release_servo(servo_id) Function: release a servo
5.4 focus_servo(servo_id)    Function: power on designated servo

 

gpt 보고 짜달라니 잘 짜준다

#!/usr/bin/env python3

import curses
import time

from pymycobot.mycobot import MyCobot
from pymycobot import PI_PORT, PI_BAUD

# -------------------------------------------------
# myCobot
# -------------------------------------------------
mc = MyCobot(PI_PORT, PI_BAUD)

SPEED = 40
STEP = 2.0        # mm
ROT_STEP = 2.0    # degree

released = True

mc.power_on()
time.sleep(1)

coords = mc.get_coords()

if coords is None:
    print("Cannot read robot coordinates.")
    exit(1)


# -------------------------------------------------
def draw_screen(stdscr, angles):

    stdscr.clear()

    stdscr.addstr(0, 0, "myCobot 280 Jog Controller")

    stdscr.addstr(2, 0, "A/D : X -/+")
    stdscr.addstr(3, 0, "W/S : Y +/-")
    stdscr.addstr(4, 0, "R/F : Z +/-")
    stdscr.addstr(5, 0, "Q/E : RX -/+")
    stdscr.addstr(6, 0, "ESC : Exit")

    stdscr.addstr(8, 0, f"Servo : {'LOCK' if released else 'RELEASE'}")

    stdscr.addstr(
        10, 0,
        f"X={coords[0]:7.2f}  "
        f"Y={coords[1]:7.2f}  "
        f"Z={coords[2]:7.2f}"
    )

    stdscr.addstr(
        11, 0,
        f"RX={coords[3]:7.2f}  "
        f"RY={coords[4]:7.2f}  "
        f"RZ={coords[5]:7.2f}"
    )

    if angles is not None:
        stdscr.addstr(13, 0, "Joint Angles")

        for i, a in enumerate(angles):
            stdscr.addstr(14+i, 0, f"J{i+1}: {a:7.2f}")

    stdscr.refresh()


# -------------------------------------------------
def main(stdscr):

    global coords
    global released

    curses.cbreak()
    curses.noecho()

    stdscr.nodelay(True)
    stdscr.keypad(True)

    angles = mc.get_angles()

    draw_screen(stdscr, angles)

    while True:

        # ------------------------------------------
        # Atom Button
        # ------------------------------------------

        button = mc.get_digital_input(39)

        if button == 0 and released:

            released = False

            mc.release_all_servos()

            draw_screen(stdscr, angles)

        elif button == 1 and not released:

            released = True

            mc.power_on()

            time.sleep(0.5)

            # Servo를 손으로 움직였을 수 있으므로
            # 이때만 실제 좌표를 다시 읽음
            new_coords = mc.get_coords()
            if new_coords is not None:
                coords = new_coords

            angles = mc.get_angles()

            draw_screen(stdscr, angles)

        key = stdscr.getch()

        if key == 27:
            break

        moved = False

        if released:

            if key == ord('a'):
                coords[0] -= STEP
                moved = True

            elif key == ord('d'):
                coords[0] += STEP
                moved = True

            elif key == ord('w'):
                coords[1] += STEP
                moved = True

            elif key == ord('s'):
                coords[1] -= STEP
                moved = True

            elif key == ord('r'):
                coords[2] += STEP
                moved = True

            elif key == ord('f'):
                coords[2] -= STEP
                moved = True

            elif key == ord('q'):
                coords[3] -= ROT_STEP
                moved = True

            elif key == ord('e'):
                coords[3] += ROT_STEP
                moved = True

            if moved:
                mc.send_coords(coords, SPEED, 1)
                draw_screen(stdscr, angles)

        time.sleep(0.02)


# -------------------------------------------------
if __name__ == "__main__":
    curses.wrapper(main)

 

[링크 : https://docs.elephantrobotics.com/docs/gitbook-en/7-ApplicationBasePython/7.2_API.html]

'embeded > robot' 카테고리의 다른 글

mycobot280 pi / TCP(tool center point)  (0) 2026.06.30
왼손 오른손 좌표계  (0) 2026.06.22
mycobot280_pi urdf  (0) 2026.06.22
블루로봇 밸런스 스테이지  (0) 2026.06.21
elephant robotics cobot python api  (0) 2026.06.19
Posted by 구차니
Programming/qt2026. 6. 29. 14:57

열심히 AI 갈구면서 테스트

 

아래의 클래스 추가

// UIScale.h
#pragma once

#include <QWidget>

namespace UIScale
{
    void scale(QWidget *w);
    void scaleTree(QWidget *root);
}

 

// UIScale.cpp

#include "UIScale.h"

#include <QGuiApplication>
#include <QScreen>
#include <QDebug>

namespace
{
    constexpr int BASE_WIDTH  = 800;
    constexpr int BASE_HEIGHT = 480;
}

void UIScale::scale(QWidget *w)
{
    if (!w)
        return;

    // 이미 적용했으면 종료
    if (w->property("_scaled").toBool())
        return;

    QScreen *screen = QGuiApplication::primaryScreen();
    if (!screen)
        return;

    QSize screenSize = screen->availableGeometry().size();

    const double sx = double(screenSize.width())  / BASE_WIDTH;
    const double sy = double(screenSize.height()) / BASE_HEIGHT;

    QRect r = w->geometry();

    w->setGeometry(
        int(r.x()      * sx),
        int(r.y()      * sy),
        int(r.width()  * sx),
        int(r.height() * sy));

    w->setProperty("_scaled", true);
}

void UIScale::scaleTree(QWidget *root)
{
    scale(root);

    const auto widgets = root->findChildren<QWidget *>();

    for (QWidget *w : widgets)
    {
        qDebug().noquote()
        << QString("%1")
              .arg(w->objectName());
        scale(w);
    }
}

 

해당 위젯의 showEvent()를 오버라이드 해서 사용한다.

scaleTree()만 실행해도 잘된다.(어짜피 tree에서 순회하면서 쭈욱 여는거라)

그런데 widget 들은 확대되서 잘 나오는데

stylesheet에서 background로 박아둔 녀석도 같이 확대되서 이상하게 작동하는 지라

스타일시트 엔진(?)은 따로 움직여서 손을 봐줘야 한다고.

void cycle::showEvent(QShowEvent *event)
{
    qDebug() << "helo";
    UIScale::scaleTree(this);
    this->update();
    this->repaint();

    QString ss = this->styleSheet();
    this->setStyleSheet("");
    this->setStyleSheet(ss);
}

[링크 : https://chatgpt.com/share/6a420896-c94c-83e8-9459-3c016987a64f]

'Programming > qt' 카테고리의 다른 글

qt5 qml Q_PROPERTY  (0) 2026.06.30
qt5 qml connections  (0) 2026.06.30
QT 다국어 지원, qm 만 교체 할수 있도록 변경  (0) 2026.06.29
QGraphicsProxyWidget  (0) 2026.06.05
QT 다국어 언어 설정 전파  (0) 2026.06.05
Posted by 구차니
Programming/qt2026. 6. 29. 10:49

현재 사용중인 코드는 좀 다르긴한데

잘 보면 :/i18n 으로 해서 리소스 파일에서 끌어오게 되어있다.

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QTranslator myappTranslator;
    if (myappTranslator.load(QLocale::system(), u"myapp"_s, u"_"_s, u":/i18n"_s))
        app.installTranslator(&myappTranslator);

    return app.exec();
}

[링크 : https://doc.qt.io/qt-6/ko/i18n-source-translation.html]

 

그러면.. 이 경로만 바꾸어 주고 빌드 시스템에서

lrealse를 수동으로 해주면 이미지와 함께 배포하게 하면 충분히 하나로 합치지 않고 배포가 가능할 것 같긴한데..

 

빌드 디렉토리에서 i18n 으로 검색해보니 qrc 관련해서 우겨 넣는게 보인다.

$ grep -rn i18n .
grep: ./main.o: 바이너리 파일 일치함
./qrc_qmake_qmake_qm_files.cpp:3903:  // i18n
./qrc_qmake_qmake_qm_files.cpp:3963:  // :/i18n
./qrc_qmake_qmake_qm_files.cpp:3966:  // :/i18n/untitled_pt_PT.qm
./qrc_qmake_qmake_qm_files.cpp:3969:  // :/i18n/untitled_ko_KR.qm
./qrc_qmake_qmake_qm_files.cpp:3972:  // :/i18n/untitled_fr_FR.qm
./qrc_qmake_qmake_qm_files.cpp:3975:  // :/i18n/untitled_sk_SK.qm
./qrc_qmake_qmake_qm_files.cpp:3978:  // :/i18n/untitled_en_US.qm
./qrc_qmake_qmake_qm_files.cpp:3981:  // :/i18n/untitled_el_GR.qm
./qrc_qmake_qmake_qm_files.cpp:3984:  // :/i18n/untitled_ru_RU.qm
./qrc_qmake_qmake_qm_files.cpp:3987:  // :/i18n/untitled_es_ES.qm
./qmake_qmake_qm_files.qrc:2:<qresource prefix="i18n">

 

ts-files에 이름을 넣고 -qm에 생성될 파일을 넣으면되니까 어찌 되려나?

$ lrelease --help
Usage:
    lrelease [options] -project project-file
    lrelease [options] ts-files [-qm qm-file]

lrelease is part of Qt's Linguist tool chain. It can be used as a
stand-alone tool to convert XML-based translations files in the TS
format into the 'compiled' QM format used by QTranslator objects.

Passing .pro files to lrelease is deprecated.
Please use the lrelease-pro tool instead, or use qmake's lrelease.prf
feature.

Options:
    -help  Display this information and exit
    -idbased
           Use IDs instead of source strings for message keying
    -compress
           Compress the QM files
    -nounfinished
           Do not include unfinished translations
    -removeidentical
           If the translated text is the same as
           the source text, do not include the message
    -markuntranslated <prefix>
           If a message has no real translation, use the source text
           prefixed with the given string instead
    -project <filename>
           Name of a file containing the project's description in JSON format.
           Such a file may be generated from a .pro file using the lprodump tool.
    -silent
           Do not explain what is being done
    -version
           Display the version of lrelease and exit

[링크 : https://chatgpt.com/share/6a41cebb-e8bc-83ee-9207-6d9ef47ff0ca]

 

일단은.. 파일 validation을 넣고 실행하게 해야 안전할 것 같은데..

'Programming > qt' 카테고리의 다른 글

qt5 qml connections  (0) 2026.06.30
qt 동적 해상도 대응  (0) 2026.06.29
QGraphicsProxyWidget  (0) 2026.06.05
QT 다국어 언어 설정 전파  (0) 2026.06.05
QT 자식 위젯으로 생성 / 부모 위젯 연결  (0) 2026.06.05
Posted by 구차니