| 
 | |||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD | ||||||||
java.lang.Objectohmm.CvBase
ohmm.CvUndistort
ohmm.CvVisualServo
public class CvVisualServo
Visual servoing to grasp a colored object (CS4610 LAB4 solution).
Uses CvBase for imaging and Grasp for arm control, and
 uses ImageServer to serve debug images.
Basic operation proceeds through the following series of states:
RESET - (return to) setup poseINIT - wait for user to click on imageCAL - determine calibration color on next frameWAIT - wait for runPending user to hit 'r'ORIENT - visual servo orientationDRIVE - visual servo fwd/reverseGRASP - arm kinematic sequence to acquire objectHOMING - drive to orig pose (if enableHoming)DONE - quits main loopThe calibration color is determined as the average over a square of
 radius calRad pixels about the clicked pixel.  The track point
 is then initialized as the calibration click point.
At every state after CAL the image is processed to look for an object
 with the calibration color.  If found, its center coordinates are saved in
 trackX, trackY.  The image processing steps are:
openIt iters ea (morphological opening)Visual servoing is two-level bang-bang control first in yaw (orientation), then in radial drive position. Orientation and drive servo phases cycle until the goal is within tol. If the object is initially too low in the frame (i.e. too close to the robot) then the first yaw phase is skipped to avoid collision with the object.
Action does not begin until calling CvBase.mainLoop(), which then blocks
 until DONE.  Many instance fields, including state, can be
 customized; these are declared public volatile so they may be
 changed in threads other than the one running CvBase.mainLoop() (more
 advanced syncronization is not needed here because the possibility to
 introduce inconsistencies across fields is low and the potential effects of
 any such inconsistency should be benign).  Handling of each state is also
 broken out into separate protected functions that could be
 overridden in subclasses.
The alternate constructor CvVisualServo(Grasp, int, int, int) is
 one convenient way to skip the interactive calibration states and also the
 final homing behavior.
| Field Summary | |
|---|---|
| static java.lang.String | APPNAMEApplication name. | 
| protected  java.lang.Thread | armThreadThread for running arm motions. | 
|  int | ballDiameterBall diameter in world frame mm, see estimateBall(). | 
|  int | ballHeightBall Z coord in world frame mm, see estimateBall(). | 
| protected  com.googlecode.javacv.cpp.opencv_core.IplImage | binAltImgWork images. | 
| protected  com.googlecode.javacv.cpp.opencv_core.IplImage | binImgWork images. | 
| static int | CALVisual servo state. | 
|  int | calBCalibration pixel. | 
|  int | calGCalibration pixel. | 
|  int | calHCalibration pixel. | 
|  int | calMinCalibration gather box radius and min pixels. | 
|  int | calRCalibration pixel. | 
|  int | calRadCalibration gather box radius and min pixels. | 
|  int | calSCalibration pixel. | 
|  int | calVCalibration pixel. | 
|  int | calXCalibration pixel. | 
|  int | calYCalibration pixel. | 
| protected  com.googlecode.javacv.cpp.opencv_core.CvSeq | contourFirst contour in sequence. | 
| static int | DBG_RDebug + radius in pixels. | 
| protected  com.googlecode.javacv.cpp.opencv_core.IplImage | dbgAltImgWork images. | 
| protected  int | dbgFrameCountdownCountdown to a debug frame. | 
|  int | dbgFreqDebug image frequency relative to process(com.googlecode.javacv.cpp.opencv_core.IplImage)frequency. | 
|  int | dbgHDebug image dims. | 
| protected  com.googlecode.javacv.cpp.opencv_core.IplImage | dbgImgWork images. | 
|  int | dbgWDebug image dims. | 
| static int | DEF_BALL_DIAMETERDefault ballDiameter. | 
| static int | DEF_BALL_HEIGHTDefault ballHeight. | 
| static int | DEF_CAL_MINDefault calRad,calMin. | 
| static int | DEF_CAL_RADDefault calRad,calMin. | 
| static int | DEF_CAMDefault camera index. | 
| static int | DEF_DBG_FREQDefault dbgFreq. | 
| static int | DEF_DBG_HDefault dbgW,dbgH. | 
| static int | DEF_DBG_WDefault dbgW,dbgH. | 
| static int | DEF_DRIVE_DONE_THRESHDefault servo state pixel thresholds. | 
| static float | DEF_DRIVE_FASTDefault straight drive speeds in mm/sec. | 
| static float | DEF_DRIVE_SLOWDefault straight drive speeds in mm/sec. | 
| static int | DEF_DRIVE_SLOW_THRESHDefault servo state pixel thresholds. | 
| static float | DEF_EXTEND_XDefault EE location in robot frame mm at retract/reach pose. | 
| static float | DEF_EXTEND_ZDefault EE location in robot frame mm at retract/reach pose. | 
| static int | DEF_H_TOLDefault visual servo default threshold parameters. | 
| static int | DEF_IMG_HDefault image size; must be set in call to CvBase.init(int, java.lang.String[]). | 
| static int | DEF_IMG_WDefault image size; must be set in call to CvBase.init(int, java.lang.String[]). | 
| static int | DEF_MAX_DIFF_TRACKDefault maxDiffTrack. | 
| static double | DEF_MIN_SERVO_TIMEDefault minServoTime. | 
| static int | DEF_OPEN_ITDefault visual servo morphological open iterations. | 
| static int | DEF_ORIENT_DONE_THRESHDefault servo state pixel thresholds. | 
| static float | DEF_ORIENT_FASTDefault turn in place speeds in rad/sec. | 
| static float | DEF_ORIENT_SLOWDefault turn in place speeds in rad/sec. | 
| static int | DEF_ORIENT_SLOW_THRESHDefault servo state pixel thresholds. | 
| static float | DEF_RETRACT_XDefault EE location in robot frame mm at retract/reach pose. | 
| static float | DEF_RETRACT_ZDefault EE location in robot frame mm at retract/reach pose. | 
| static int | DEF_S_MAXDefault visual servo default threshold parameters. | 
| static int | DEF_S_MINDefault visual servo default threshold parameters. | 
| static float | DEF_T0_UPDefault joints in rad in the visual servo "up" pose. | 
| static float | DEF_T1_UPDefault joints in rad in the visual servo "up" pose. | 
| static float | DEF_T2_UPDefault joints in rad in the visual servo "up" pose. | 
| static int | DEF_TRACK_D_FUDGEDefault trackDFudge. | 
| static int | DEF_V_MAXDefault visual servo default threshold parameters. | 
| static int | DEF_V_MINDefault visual servo default threshold parameters. | 
| static int | DONEVisual servo state. | 
| static int | DRIVEVisual servo state. | 
|  int | driveDoneThreshServo state pixel thresholds. | 
|  float | driveFastStraight drive speeds in mm/sec. | 
|  float | driveSlowStraight drive speeds in mm/sec. | 
|  int | driveSlowThreshServo state pixel thresholds. | 
|  boolean | enableHomingWhether to enable the HOMINGstate. | 
|  float | extendXEE location in robot frame mm at reach pose. | 
|  float | extendZEE location in robot frame mm at reach pose. | 
|  Grasp | graspThe Graspobject. | 
| static int | GRASPVisual servo state. | 
|  int | hAltMaxThreshold parameters. | 
|  int | hAltMinThreshold parameters. | 
|  int | hMaxThreshold parameters. | 
|  int | hMinThreshold parameters. | 
| static int | HOMINGVisual servo state. | 
|  com.googlecode.javacv.cpp.opencv_core.CvScalar | hsvAltMaxHSV threshold bounds. | 
|  com.googlecode.javacv.cpp.opencv_core.CvScalar | hsvAltMinHSV threshold bounds. | 
| protected  com.googlecode.javacv.cpp.opencv_core.IplImage | hsvImgWork images. | 
|  com.googlecode.javacv.cpp.opencv_core.CvScalar | hsvMaxHSV threshold bounds. | 
|  com.googlecode.javacv.cpp.opencv_core.CvScalar | hsvMinHSV threshold bounds. | 
|  int | hTolThreshold parameters. | 
| protected  int | imgHImage dims, detected on first call to process(com.googlecode.javacv.cpp.opencv_core.IplImage). | 
| protected  int | imgWImage dims, detected on first call to process(com.googlecode.javacv.cpp.opencv_core.IplImage). | 
| static int | INITVisual servo state. | 
| protected  float[] | initXYTInitial robot pose. | 
| protected  double | lastServoTimeTime of last servo update. | 
|  int | maxDiffTrackMaximum change in track point in pixels. | 
|  double | minServoTimeMinimum time between servo updates in seconds. | 
|  int | mouseXMouse location in image or (-1, -1) if none. | 
|  int | mouseYMouse location in image or (-1, -1) if none. | 
|  OHMMDrive | ohmmThe OHMMDriveobject. | 
|  int | openItMorphological open iterations. | 
| static int | ORIENTVisual servo state. | 
|  int | orientDoneThreshServo state pixel thresholds. | 
|  float | orientFastTurn in place speeds in rad/sec. | 
|  float | orientSlowTurn in place speeds in rad/sec. | 
|  int | orientSlowThreshServo state pixel thresholds. | 
| static int | RESETVisual servo state. | 
|  float | retractXEE location in robot frame mm at retract pose. | 
|  float | retractZEE location in robot frame mm at retract pose. | 
|  boolean | runPendingWhether the ORIENTstate is pending. | 
| static java.lang.String | RXTX_PORTRXTX port filename for test driver only. | 
|  int | sMaxThreshold parameters. | 
|  int | sMinThreshold parameters. | 
|  int | stateState machine state, see class header doc. | 
| protected  com.googlecode.javacv.cpp.opencv_core.CvMemStorage | storageFor storing contours. | 
|  float | t0UpArm joints in rad in the visual servo "up" pose. | 
|  float | t1UpArm joints in rad in the visual servo "up" pose. | 
|  float | t2UpArm joints in rad in the visual servo "up" pose. | 
|  int | trackDTracked blob diameter or -1 if no tracking, see estimateBall(). | 
|  int | trackDFudgeFudge factor for trackD. | 
|  int | trackXTracked pixel or (-1, -1) for no tracking. | 
|  int | trackYTracked pixel or (-1, -1) for no tracking. | 
|  int | vMaxThreshold parameters. | 
|  int | vMinThreshold parameters. | 
| static int | WAITVisual servo state. | 
| Fields inherited from class ohmm.CvUndistort | 
|---|
| distortions, extrinsics, intrinsics, mapx, mapy, undistort, undistortImage | 
| Constructor Summary | |
|---|---|
| CvVisualServo(Grasp grasp)Inits frame processing. | |
| CvVisualServo(Grasp grasp,
              int calH,
              int calX,
              int calY)Convenience constructor to run non-interactive. | |
| Method Summary | |
|---|---|
|  void | abort()reset()and setstateDONE | 
| protected  boolean | doneProcessing()This impl checks if stateisDONE. | 
| protected  void | estimateBall()Estimate ball coordinates in robot frame (optional). | 
| protected  void | guiHelpExt()Show help for custom keypresses. | 
| protected  boolean | handleKeyExt(int code)Handle custom keypresses. | 
| protected  void | handleMouse(int event,
            int x,
            int y,
            int flags)Handle mouse clicks. | 
| static void | main(java.lang.String[] argv)Test driver. | 
| protected  com.googlecode.javacv.cpp.opencv_core.IplImage | process(com.googlecode.javacv.cpp.opencv_core.IplImage frame)Do all the frame processing and servoing. | 
| protected  void | processCal(com.googlecode.javacv.cpp.opencv_core.IplImage frame)Handle the indicated stateinprocess(com.googlecode.javacv.cpp.opencv_core.IplImage). | 
| protected  void | processDrive(com.googlecode.javacv.cpp.opencv_core.IplImage frame)Handle the indicated stateinprocess(com.googlecode.javacv.cpp.opencv_core.IplImage). | 
| protected  void | processGrasp(com.googlecode.javacv.cpp.opencv_core.IplImage frame)Handle the indicated stateinprocess(com.googlecode.javacv.cpp.opencv_core.IplImage). | 
| protected  void | processHoming(com.googlecode.javacv.cpp.opencv_core.IplImage frame)Handle the indicated stateinprocess(com.googlecode.javacv.cpp.opencv_core.IplImage). | 
| protected  void | processOrient(com.googlecode.javacv.cpp.opencv_core.IplImage frame)Handle the indicated stateinprocess(com.googlecode.javacv.cpp.opencv_core.IplImage). | 
| protected  void | processReset(com.googlecode.javacv.cpp.opencv_core.IplImage frame)Handle the indicated stateinprocess(com.googlecode.javacv.cpp.opencv_core.IplImage). | 
| protected  com.googlecode.javacv.cpp.opencv_core.IplImage | processUpdateReturnImge(com.googlecode.javacv.cpp.opencv_core.IplImage frame)Create/update the return image for process(com.googlecode.javacv.cpp.opencv_core.IplImage). | 
| protected  void | processUpdateTracking(com.googlecode.javacv.cpp.opencv_core.IplImage frame)Update trackX,trackYinprocess(com.googlecode.javacv.cpp.opencv_core.IplImage). | 
| protected  void | processWait(com.googlecode.javacv.cpp.opencv_core.IplImage frame)Handle the indicated stateinprocess(com.googlecode.javacv.cpp.opencv_core.IplImage). | 
|  void | reset()Reset to initialization state. | 
|  void | updateThresh()Recalculate hsvMinandhsvMaxfromcalHandhTol. | 
| Methods inherited from class ohmm.CvUndistort | 
|---|
| camIndexOptional, cmdHelpExt, cmdHelpExtParams, initExt, readMat, release | 
| Methods inherited from class ohmm.CvBase | 
|---|
| cmdHelp, dumpCaptureProperties, finalize, fmt, getCaptureProperty, getDefAppname, getDefInput, guiHelp, handleKey, handleMouse, init, init, init, init, mainLoop, makeGrabber, makeServer, mouseEventToString, msg, nowMS, save, setCaptureProperty, updateServerImage, v4l2DisableAuto, v4l2EnableAuto, v4l2GetExposure, v4l2SetExposure, waitForKeypresss, warn | 
| Methods inherited from class java.lang.Object | 
|---|
| clone, equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait | 
| Field Detail | 
|---|
public static final java.lang.String APPNAME
public static final int DEF_CAM
public static final int DEF_IMG_W
CvBase.init(int, java.lang.String[]).
public static final int DEF_IMG_H
CvBase.init(int, java.lang.String[]).
public static final int DEF_DBG_W
dbgW, dbgH.
public static final int DEF_DBG_H
dbgW, dbgH.
public static final int DEF_DBG_FREQ
dbgFreq.
public static final int DBG_R
public static final int RESET
public static final int INIT
public static final int CAL
public static final int WAIT
public static final int ORIENT
public static final int DRIVE
public static final int GRASP
public static final int HOMING
public static final int DONE
public static final int DEF_CAL_RAD
calRad, calMin.
public static final int DEF_CAL_MIN
calRad, calMin.
public static final int DEF_MAX_DIFF_TRACK
maxDiffTrack.
public static final int DEF_H_TOL
public static final int DEF_S_MIN
public static final int DEF_S_MAX
public static final int DEF_V_MIN
public static final int DEF_V_MAX
public static final int DEF_OPEN_IT
public static final int DEF_ORIENT_DONE_THRESH
public static final int DEF_ORIENT_SLOW_THRESH
public static final int DEF_DRIVE_DONE_THRESH
public static final int DEF_DRIVE_SLOW_THRESH
public static final float DEF_ORIENT_SLOW
public static final float DEF_ORIENT_FAST
public static final float DEF_DRIVE_SLOW
public static final float DEF_DRIVE_FAST
public static final double DEF_MIN_SERVO_TIME
minServoTime.
public static final float DEF_EXTEND_X
public static final float DEF_RETRACT_X
public static final float DEF_RETRACT_Z
public static final float DEF_EXTEND_Z
public static final float DEF_T0_UP
public static final float DEF_T1_UP
public static final float DEF_T2_UP
public static final java.lang.String RXTX_PORT
public static final int DEF_BALL_HEIGHT
ballHeight.
public static final int DEF_BALL_DIAMETER
ballDiameter.
public static final int DEF_TRACK_D_FUDGE
trackDFudge.
public volatile int calRad
public volatile int calMin
public volatile int maxDiffTrack
public volatile int orientDoneThresh
public volatile int orientSlowThresh
public volatile int driveDoneThresh
public volatile int driveSlowThresh
public volatile float orientSlow
public volatile float orientFast
public volatile float driveSlow
public volatile float driveFast
public volatile double minServoTime
public volatile float retractX
public volatile float retractZ
public volatile float extendX
public volatile float extendZ
public volatile float t0Up
public volatile float t1Up
public volatile float t2Up
public final Grasp grasp
Grasp object.
public final OHMMDrive ohmm
OHMMDrive object.
public volatile int state
public volatile boolean runPending
ORIENT state is pending.
public volatile boolean enableHoming
HOMING state.
public volatile int calX
public volatile int calY
public volatile int calR
public volatile int calG
public volatile int calB
public volatile int calH
public volatile int calS
public volatile int calV
public volatile int trackX
public volatile int trackY
public volatile int trackD
estimateBall().
public volatile int ballHeight
estimateBall().
public volatile int ballDiameter
estimateBall().
public volatile int trackDFudge
trackD.
public volatile int mouseX
public volatile int mouseY
public volatile int hTol
public volatile int hMin
public volatile int hMax
public volatile int hAltMin
public volatile int hAltMax
public volatile int sMin
public volatile int sMax
public volatile int vMin
public volatile int vMax
public volatile int openIt
public volatile com.googlecode.javacv.cpp.opencv_core.CvScalar hsvMin
public volatile com.googlecode.javacv.cpp.opencv_core.CvScalar hsvMax
public volatile com.googlecode.javacv.cpp.opencv_core.CvScalar hsvAltMin
public volatile com.googlecode.javacv.cpp.opencv_core.CvScalar hsvAltMax
public volatile int dbgW
public volatile int dbgH
public volatile int dbgFreq
process(com.googlecode.javacv.cpp.opencv_core.IplImage) frequency.
protected volatile com.googlecode.javacv.cpp.opencv_core.CvMemStorage storage
protected volatile com.googlecode.javacv.cpp.opencv_core.CvSeq contour
protected volatile com.googlecode.javacv.cpp.opencv_core.IplImage hsvImg
protected volatile com.googlecode.javacv.cpp.opencv_core.IplImage dbgImg
protected volatile com.googlecode.javacv.cpp.opencv_core.IplImage dbgAltImg
protected volatile com.googlecode.javacv.cpp.opencv_core.IplImage binImg
protected volatile com.googlecode.javacv.cpp.opencv_core.IplImage binAltImg
protected volatile int dbgFrameCountdown
protected volatile java.lang.Thread armThread
protected volatile double lastServoTime
protected volatile float[] initXYT
protected volatile int imgW
process(com.googlecode.javacv.cpp.opencv_core.IplImage).
protected volatile int imgH
process(com.googlecode.javacv.cpp.opencv_core.IplImage).
| Constructor Detail | 
|---|
public CvVisualServo(Grasp grasp)
grasp - the Grasp object to control the robot
 You must call one of the init() APIs and then CvBase.mainLoop() to complete setup and then start processing frames.
Sets defaults:
CvBase.maxFPS = 10CvBase.serverFPS = 2Change these (and other config fields) before calling
 init() for customization.
public CvVisualServo(Grasp grasp,
                     int calH,
                     int calX,
                     int calY)
grasp - the Grasp object to control the robotcalH - the calibration huecalX - the calibration X pixelcalY - the calibration Y pixel
 Inits calH, calX and calY then calls
 updateThresh(), thus skipping interactive calibration.  Then sets
 initial state to WAIT with runPending = true, which
 has the effect of stopping any ongoing drive motion (without resetting
 odometry), opening the gripper and moving the arm to the lowered retract
 pose, and then continuing to state ORIENT.  Sets enableHoming = false so that CvBase.mainLoop() will exit after
 grasping.
See CvVisualServo(Grasp) for further info.
| Method Detail | 
|---|
public void reset()
public void abort()
reset() and set state DONE
public void updateThresh()
protected boolean handleKeyExt(int code)
handleKeyExt in class CvUndistortprotected void guiHelpExt()
guiHelpExt in class CvUndistort
protected void handleMouse(int event,
                           int x,
                           int y,
                           int flags)
handleMouse in class CvBaseevent - one of the CV_EVENT_* constants (see opencv_highgui.java in
 the JavaCV sources for a list)x - the x pixel coordinate of the mouse eventy - the y pixel coordinate of the mouse eventflags - bitmask of the CV_EVENT_FLAG_* constants (see
 opencv_highgui.java in the JavaCV sources for a list)
 Default impl prints a message using CvBase.mouseEventToString(int, int, int, int) except
 for CV_EVENT_MOUSEMOVE.
Overriding this is one way that subclasses can handle mouse events.
protected com.googlecode.javacv.cpp.opencv_core.IplImage process(com.googlecode.javacv.cpp.opencv_core.IplImage frame)
Do all the frame processing and servoing.
See class header doc for details.
process in class CvUndistortprotected void processReset(com.googlecode.javacv.cpp.opencv_core.IplImage frame)
Handle the indicated state in process(com.googlecode.javacv.cpp.opencv_core.IplImage).
frame - the image passed to process(com.googlecode.javacv.cpp.opencv_core.IplImage)protected void processCal(com.googlecode.javacv.cpp.opencv_core.IplImage frame)
Handle the indicated state in process(com.googlecode.javacv.cpp.opencv_core.IplImage).
frame - the image passed to process(com.googlecode.javacv.cpp.opencv_core.IplImage)protected void processWait(com.googlecode.javacv.cpp.opencv_core.IplImage frame)
Handle the indicated state in process(com.googlecode.javacv.cpp.opencv_core.IplImage).
frame - the image passed to process(com.googlecode.javacv.cpp.opencv_core.IplImage)protected void processUpdateTracking(com.googlecode.javacv.cpp.opencv_core.IplImage frame)
Update trackX, trackY in process(com.googlecode.javacv.cpp.opencv_core.IplImage).
Called iff state is at least WAIT.
frame - the image passed to process(com.googlecode.javacv.cpp.opencv_core.IplImage)protected void processOrient(com.googlecode.javacv.cpp.opencv_core.IplImage frame)
Handle the indicated state in process(com.googlecode.javacv.cpp.opencv_core.IplImage).
frame - the image passed to process(com.googlecode.javacv.cpp.opencv_core.IplImage)protected void processDrive(com.googlecode.javacv.cpp.opencv_core.IplImage frame)
Handle the indicated state in process(com.googlecode.javacv.cpp.opencv_core.IplImage).
frame - the image passed to process(com.googlecode.javacv.cpp.opencv_core.IplImage)protected void processGrasp(com.googlecode.javacv.cpp.opencv_core.IplImage frame)
Handle the indicated state in process(com.googlecode.javacv.cpp.opencv_core.IplImage).
frame - the image passed to process(com.googlecode.javacv.cpp.opencv_core.IplImage)protected void processHoming(com.googlecode.javacv.cpp.opencv_core.IplImage frame)
Handle the indicated state in process(com.googlecode.javacv.cpp.opencv_core.IplImage).
frame - the image passed to process(com.googlecode.javacv.cpp.opencv_core.IplImage)protected com.googlecode.javacv.cpp.opencv_core.IplImage processUpdateReturnImge(com.googlecode.javacv.cpp.opencv_core.IplImage frame)
Create/update the return image for process(com.googlecode.javacv.cpp.opencv_core.IplImage).
frame - the image passed to process(com.googlecode.javacv.cpp.opencv_core.IplImage)
protected boolean doneProcessing()
This impl checks if state is DONE.
doneProcessing in class CvBaseDefault impl returns false.
protected void estimateBall()
Estimate ball coordinates in robot frame (optional).
The main visual servo algorithm does not require this estimate and does not use this method, which is for demonstration only.
Demonstrates two approaches to estimate the 3D ball coordinates in
 robot frame mm.  One is based on the assumption that the center of the
 ball is at ballHeight mm above the ground.  The other assumes the
 ball diameter is ballDiameter mm and estimates the apparrent
 diameter in pixels.
Both methods require the camera matrix CvUndistort.intrinsics and the
 camera pose CvUndistort.extrinsics in robot frame, which are only available
 if a camera calibration file was specified on the command line.
public static void main(java.lang.String[] argv)
Test driver.
| 
 | |||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD | ||||||||